1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Based on drivers/video/omap/lcd_inn1510.c |
4 | * |
5 | * LCD panel support for the Amstrad E3 (Delta) videophone. |
6 | * |
7 | * Copyright (C) 2006 Jonathan McDowell <noodles@earth.li> |
8 | */ |
9 | |
10 | #include <linux/module.h> |
11 | #include <linux/platform_device.h> |
12 | #include <linux/io.h> |
13 | #include <linux/delay.h> |
14 | #include <linux/gpio/consumer.h> |
15 | #include <linux/lcd.h> |
16 | |
17 | #include <linux/soc/ti/omap1-io.h> |
18 | |
19 | #include "omapfb.h" |
20 | |
21 | #define AMS_DELTA_DEFAULT_CONTRAST 112 |
22 | |
23 | #define AMS_DELTA_MAX_CONTRAST 0x00FF |
24 | #define AMS_DELTA_LCD_POWER 0x0100 |
25 | |
26 | |
27 | /* LCD class device section */ |
28 | |
29 | static int ams_delta_lcd; |
30 | static struct gpio_desc *gpiod_vblen; |
31 | static struct gpio_desc *gpiod_ndisp; |
32 | |
33 | static int ams_delta_lcd_set_power(struct lcd_device *dev, int power) |
34 | { |
35 | if (power == FB_BLANK_UNBLANK) { |
36 | if (!(ams_delta_lcd & AMS_DELTA_LCD_POWER)) { |
37 | omap_writeb(v: ams_delta_lcd & AMS_DELTA_MAX_CONTRAST, |
38 | OMAP_PWL_ENABLE); |
39 | omap_writeb(v: 1, OMAP_PWL_CLK_ENABLE); |
40 | ams_delta_lcd |= AMS_DELTA_LCD_POWER; |
41 | } |
42 | } else { |
43 | if (ams_delta_lcd & AMS_DELTA_LCD_POWER) { |
44 | omap_writeb(v: 0, OMAP_PWL_ENABLE); |
45 | omap_writeb(v: 0, OMAP_PWL_CLK_ENABLE); |
46 | ams_delta_lcd &= ~AMS_DELTA_LCD_POWER; |
47 | } |
48 | } |
49 | return 0; |
50 | } |
51 | |
52 | static int ams_delta_lcd_set_contrast(struct lcd_device *dev, int value) |
53 | { |
54 | if ((value >= 0) && (value <= AMS_DELTA_MAX_CONTRAST)) { |
55 | omap_writeb(v: value, OMAP_PWL_ENABLE); |
56 | ams_delta_lcd &= ~AMS_DELTA_MAX_CONTRAST; |
57 | ams_delta_lcd |= value; |
58 | } |
59 | return 0; |
60 | } |
61 | |
62 | #ifdef CONFIG_LCD_CLASS_DEVICE |
63 | static int ams_delta_lcd_get_power(struct lcd_device *dev) |
64 | { |
65 | if (ams_delta_lcd & AMS_DELTA_LCD_POWER) |
66 | return FB_BLANK_UNBLANK; |
67 | else |
68 | return FB_BLANK_POWERDOWN; |
69 | } |
70 | |
71 | static int ams_delta_lcd_get_contrast(struct lcd_device *dev) |
72 | { |
73 | if (!(ams_delta_lcd & AMS_DELTA_LCD_POWER)) |
74 | return 0; |
75 | |
76 | return ams_delta_lcd & AMS_DELTA_MAX_CONTRAST; |
77 | } |
78 | |
79 | static struct lcd_ops ams_delta_lcd_ops = { |
80 | .get_power = ams_delta_lcd_get_power, |
81 | .set_power = ams_delta_lcd_set_power, |
82 | .get_contrast = ams_delta_lcd_get_contrast, |
83 | .set_contrast = ams_delta_lcd_set_contrast, |
84 | }; |
85 | #endif |
86 | |
87 | |
88 | /* omapfb panel section */ |
89 | |
90 | static int ams_delta_panel_enable(struct lcd_panel *panel) |
91 | { |
92 | gpiod_set_value(desc: gpiod_ndisp, value: 1); |
93 | gpiod_set_value(desc: gpiod_vblen, value: 1); |
94 | return 0; |
95 | } |
96 | |
97 | static void ams_delta_panel_disable(struct lcd_panel *panel) |
98 | { |
99 | gpiod_set_value(desc: gpiod_vblen, value: 0); |
100 | gpiod_set_value(desc: gpiod_ndisp, value: 0); |
101 | } |
102 | |
103 | static struct lcd_panel ams_delta_panel = { |
104 | .name = "ams-delta" , |
105 | .config = 0, |
106 | |
107 | .bpp = 12, |
108 | .data_lines = 16, |
109 | .x_res = 480, |
110 | .y_res = 320, |
111 | .pixel_clock = 4687, |
112 | .hsw = 3, |
113 | .hfp = 1, |
114 | .hbp = 1, |
115 | .vsw = 1, |
116 | .vfp = 0, |
117 | .vbp = 0, |
118 | .pcd = 0, |
119 | .acb = 37, |
120 | |
121 | .enable = ams_delta_panel_enable, |
122 | .disable = ams_delta_panel_disable, |
123 | }; |
124 | |
125 | |
126 | /* platform driver section */ |
127 | |
128 | static int ams_delta_panel_probe(struct platform_device *pdev) |
129 | { |
130 | struct lcd_device *lcd_device = NULL; |
131 | |
132 | gpiod_vblen = devm_gpiod_get(dev: &pdev->dev, con_id: "vblen" , flags: GPIOD_OUT_LOW); |
133 | if (IS_ERR(ptr: gpiod_vblen)) |
134 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: gpiod_vblen), |
135 | fmt: "VBLEN GPIO request failed\n" ); |
136 | |
137 | gpiod_ndisp = devm_gpiod_get(dev: &pdev->dev, con_id: "ndisp" , flags: GPIOD_OUT_LOW); |
138 | if (IS_ERR(ptr: gpiod_ndisp)) |
139 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: gpiod_ndisp), |
140 | fmt: "NDISP GPIO request failed\n" ); |
141 | |
142 | #ifdef CONFIG_LCD_CLASS_DEVICE |
143 | lcd_device = lcd_device_register(name: "omapfb" , parent: &pdev->dev, NULL, |
144 | ops: &ams_delta_lcd_ops); |
145 | |
146 | if (IS_ERR(ptr: lcd_device)) { |
147 | int ret = PTR_ERR(ptr: lcd_device); |
148 | |
149 | dev_err(&pdev->dev, "failed to register device\n" ); |
150 | return ret; |
151 | } |
152 | |
153 | platform_set_drvdata(pdev, data: lcd_device); |
154 | lcd_device->props.max_contrast = AMS_DELTA_MAX_CONTRAST; |
155 | #endif |
156 | |
157 | ams_delta_lcd_set_contrast(dev: lcd_device, AMS_DELTA_DEFAULT_CONTRAST); |
158 | ams_delta_lcd_set_power(dev: lcd_device, power: FB_BLANK_UNBLANK); |
159 | |
160 | omapfb_register_panel(panel: &ams_delta_panel); |
161 | return 0; |
162 | } |
163 | |
164 | static struct platform_driver ams_delta_panel_driver = { |
165 | .probe = ams_delta_panel_probe, |
166 | .driver = { |
167 | .name = "lcd_ams_delta" , |
168 | }, |
169 | }; |
170 | |
171 | module_platform_driver(ams_delta_panel_driver); |
172 | |
173 | MODULE_AUTHOR("Jonathan McDowell <noodles@earth.li>" ); |
174 | MODULE_DESCRIPTION("LCD panel support for the Amstrad E3 (Delta) videophone" ); |
175 | MODULE_LICENSE("GPL" ); |
176 | |