diff options
30 files changed, 6911 insertions, 36 deletions
diff --git a/drivers/gpu/drm/omapdrm/omap_drv.c b/drivers/gpu/drm/omapdrm/omap_drv.c index 2c2316a59b6c..a3004f12b9a3 100644 --- a/drivers/gpu/drm/omapdrm/omap_drv.c +++ b/drivers/gpu/drm/omapdrm/omap_drv.c | |||
@@ -65,10 +65,8 @@ static int get_connector_type(struct omap_dss_device *dssdev) | |||
65 | switch (dssdev->type) { | 65 | switch (dssdev->type) { |
66 | case OMAP_DISPLAY_TYPE_HDMI: | 66 | case OMAP_DISPLAY_TYPE_HDMI: |
67 | return DRM_MODE_CONNECTOR_HDMIA; | 67 | return DRM_MODE_CONNECTOR_HDMIA; |
68 | case OMAP_DISPLAY_TYPE_DPI: | 68 | case OMAP_DISPLAY_TYPE_DVI: |
69 | if (!strcmp(dssdev->name, "dvi")) | 69 | return DRM_MODE_CONNECTOR_DVID; |
70 | return DRM_MODE_CONNECTOR_DVID; | ||
71 | /* fallthrough */ | ||
72 | default: | 70 | default: |
73 | return DRM_MODE_CONNECTOR_Unknown; | 71 | return DRM_MODE_CONNECTOR_Unknown; |
74 | } | 72 | } |
diff --git a/drivers/video/omap2/Kconfig b/drivers/video/omap2/Kconfig index b07b2b042e7e..56cad0f5386c 100644 --- a/drivers/video/omap2/Kconfig +++ b/drivers/video/omap2/Kconfig | |||
@@ -6,5 +6,6 @@ if ARCH_OMAP2PLUS | |||
6 | source "drivers/video/omap2/dss/Kconfig" | 6 | source "drivers/video/omap2/dss/Kconfig" |
7 | source "drivers/video/omap2/omapfb/Kconfig" | 7 | source "drivers/video/omap2/omapfb/Kconfig" |
8 | source "drivers/video/omap2/displays/Kconfig" | 8 | source "drivers/video/omap2/displays/Kconfig" |
9 | source "drivers/video/omap2/displays-new/Kconfig" | ||
9 | 10 | ||
10 | endif | 11 | endif |
diff --git a/drivers/video/omap2/Makefile b/drivers/video/omap2/Makefile index 296e5c5281c5..86873c2fbb27 100644 --- a/drivers/video/omap2/Makefile +++ b/drivers/video/omap2/Makefile | |||
@@ -2,4 +2,5 @@ obj-$(CONFIG_OMAP2_VRFB) += vrfb.o | |||
2 | 2 | ||
3 | obj-$(CONFIG_OMAP2_DSS) += dss/ | 3 | obj-$(CONFIG_OMAP2_DSS) += dss/ |
4 | obj-y += displays/ | 4 | obj-y += displays/ |
5 | obj-y += displays-new/ | ||
5 | obj-$(CONFIG_FB_OMAP2) += omapfb/ | 6 | obj-$(CONFIG_FB_OMAP2) += omapfb/ |
diff --git a/drivers/video/omap2/displays-new/Kconfig b/drivers/video/omap2/displays-new/Kconfig new file mode 100644 index 000000000000..6c90885b0940 --- /dev/null +++ b/drivers/video/omap2/displays-new/Kconfig | |||
@@ -0,0 +1,73 @@ | |||
1 | menu "OMAP Display Device Drivers (new device model)" | ||
2 | depends on OMAP2_DSS | ||
3 | |||
4 | config DISPLAY_ENCODER_TFP410 | ||
5 | tristate "TFP410 DPI to DVI Encoder" | ||
6 | help | ||
7 | Driver for TFP410 DPI to DVI encoder. | ||
8 | |||
9 | config DISPLAY_ENCODER_TPD12S015 | ||
10 | tristate "TPD12S015 HDMI ESD protection and level shifter" | ||
11 | help | ||
12 | Driver for TPD12S015, which offers HDMI ESD protection and level | ||
13 | shifting. | ||
14 | |||
15 | config DISPLAY_CONNECTOR_DVI | ||
16 | tristate "DVI Connector" | ||
17 | depends on I2C | ||
18 | help | ||
19 | Driver for a generic DVI connector. | ||
20 | |||
21 | config DISPLAY_CONNECTOR_HDMI | ||
22 | tristate "HDMI Connector" | ||
23 | help | ||
24 | Driver for a generic HDMI connector. | ||
25 | |||
26 | config DISPLAY_CONNECTOR_ANALOG_TV | ||
27 | tristate "Analog TV Connector" | ||
28 | help | ||
29 | Driver for a generic analog TV connector. | ||
30 | |||
31 | config DISPLAY_PANEL_DPI | ||
32 | tristate "Generic DPI panel" | ||
33 | help | ||
34 | Driver for generic DPI panels. | ||
35 | |||
36 | config DISPLAY_PANEL_DSI_CM | ||
37 | tristate "Generic DSI Command Mode Panel" | ||
38 | help | ||
39 | Driver for generic DSI command mode panels. | ||
40 | |||
41 | config DISPLAY_PANEL_SONY_ACX565AKM | ||
42 | tristate "ACX565AKM Panel" | ||
43 | depends on SPI && BACKLIGHT_CLASS_DEVICE | ||
44 | help | ||
45 | This is the LCD panel used on Nokia N900 | ||
46 | |||
47 | config DISPLAY_PANEL_LGPHILIPS_LB035Q02 | ||
48 | tristate "LG.Philips LB035Q02 LCD Panel" | ||
49 | depends on SPI | ||
50 | help | ||
51 | LCD Panel used on the Gumstix Overo Palo35 | ||
52 | |||
53 | config DISPLAY_PANEL_SHARP_LS037V7DW01 | ||
54 | tristate "Sharp LS037V7DW01 LCD Panel" | ||
55 | depends on BACKLIGHT_CLASS_DEVICE | ||
56 | help | ||
57 | LCD Panel used in TI's SDP3430 and EVM boards | ||
58 | |||
59 | config DISPLAY_PANEL_TPO_TD043MTEA1 | ||
60 | tristate "TPO TD043MTEA1 LCD Panel" | ||
61 | depends on SPI | ||
62 | help | ||
63 | LCD Panel used in OMAP3 Pandora | ||
64 | |||
65 | config DISPLAY_PANEL_NEC_NL8048HL11 | ||
66 | tristate "NEC NL8048HL11 Panel" | ||
67 | depends on SPI | ||
68 | depends on BACKLIGHT_CLASS_DEVICE | ||
69 | help | ||
70 | This NEC NL8048HL11 panel is TFT LCD used in the | ||
71 | Zoom2/3/3630 sdp boards. | ||
72 | |||
73 | endmenu | ||
diff --git a/drivers/video/omap2/displays-new/Makefile b/drivers/video/omap2/displays-new/Makefile new file mode 100644 index 000000000000..5aeb11b8fcd5 --- /dev/null +++ b/drivers/video/omap2/displays-new/Makefile | |||
@@ -0,0 +1,12 @@ | |||
1 | obj-$(CONFIG_DISPLAY_ENCODER_TFP410) += encoder-tfp410.o | ||
2 | obj-$(CONFIG_DISPLAY_ENCODER_TPD12S015) += encoder-tpd12s015.o | ||
3 | obj-$(CONFIG_DISPLAY_CONNECTOR_DVI) += connector-dvi.o | ||
4 | obj-$(CONFIG_DISPLAY_CONNECTOR_HDMI) += connector-hdmi.o | ||
5 | obj-$(CONFIG_DISPLAY_CONNECTOR_ANALOG_TV) += connector-analog-tv.o | ||
6 | obj-$(CONFIG_DISPLAY_PANEL_DPI) += panel-dpi.o | ||
7 | obj-$(CONFIG_DISPLAY_PANEL_DSI_CM) += panel-dsi-cm.o | ||
8 | obj-$(CONFIG_DISPLAY_PANEL_SONY_ACX565AKM) += panel-sony-acx565akm.o | ||
9 | obj-$(CONFIG_DISPLAY_PANEL_LGPHILIPS_LB035Q02) += panel-lgphilips-lb035q02.o | ||
10 | obj-$(CONFIG_DISPLAY_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o | ||
11 | obj-$(CONFIG_DISPLAY_PANEL_TPO_TD043MTEA1) += panel-tpo-td043mtea1.o | ||
12 | obj-$(CONFIG_DISPLAY_PANEL_NEC_NL8048HL11) += panel-nec-nl8048hl11.o | ||
diff --git a/drivers/video/omap2/displays-new/connector-analog-tv.c b/drivers/video/omap2/displays-new/connector-analog-tv.c new file mode 100644 index 000000000000..5338f362293b --- /dev/null +++ b/drivers/video/omap2/displays-new/connector-analog-tv.c | |||
@@ -0,0 +1,265 @@ | |||
1 | /* | ||
2 | * Analog TV Connector driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Texas Instruments | ||
5 | * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License version 2 as published by | ||
9 | * the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/slab.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | |||
16 | #include <video/omapdss.h> | ||
17 | #include <video/omap-panel-data.h> | ||
18 | |||
19 | struct panel_drv_data { | ||
20 | struct omap_dss_device dssdev; | ||
21 | struct omap_dss_device *in; | ||
22 | |||
23 | struct device *dev; | ||
24 | |||
25 | struct omap_video_timings timings; | ||
26 | |||
27 | enum omap_dss_venc_type connector_type; | ||
28 | bool invert_polarity; | ||
29 | }; | ||
30 | |||
31 | #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) | ||
32 | |||
33 | static int tvc_connect(struct omap_dss_device *dssdev) | ||
34 | { | ||
35 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
36 | struct omap_dss_device *in = ddata->in; | ||
37 | int r; | ||
38 | |||
39 | dev_dbg(ddata->dev, "connect\n"); | ||
40 | |||
41 | if (omapdss_device_is_connected(dssdev)) | ||
42 | return 0; | ||
43 | |||
44 | r = in->ops.atv->connect(in, dssdev); | ||
45 | if (r) | ||
46 | return r; | ||
47 | |||
48 | return 0; | ||
49 | } | ||
50 | |||
51 | static void tvc_disconnect(struct omap_dss_device *dssdev) | ||
52 | { | ||
53 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
54 | struct omap_dss_device *in = ddata->in; | ||
55 | |||
56 | dev_dbg(ddata->dev, "disconnect\n"); | ||
57 | |||
58 | if (!omapdss_device_is_connected(dssdev)) | ||
59 | return; | ||
60 | |||
61 | in->ops.atv->disconnect(in, dssdev); | ||
62 | } | ||
63 | |||
64 | static int tvc_enable(struct omap_dss_device *dssdev) | ||
65 | { | ||
66 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
67 | struct omap_dss_device *in = ddata->in; | ||
68 | int r; | ||
69 | |||
70 | dev_dbg(ddata->dev, "enable\n"); | ||
71 | |||
72 | if (!omapdss_device_is_connected(dssdev)) | ||
73 | return -ENODEV; | ||
74 | |||
75 | if (omapdss_device_is_enabled(dssdev)) | ||
76 | return 0; | ||
77 | |||
78 | in->ops.atv->set_timings(in, &ddata->timings); | ||
79 | |||
80 | in->ops.atv->set_type(in, ddata->connector_type); | ||
81 | in->ops.atv->invert_vid_out_polarity(in, ddata->invert_polarity); | ||
82 | |||
83 | r = in->ops.atv->enable(in); | ||
84 | if (r) | ||
85 | return r; | ||
86 | |||
87 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | ||
88 | |||
89 | return r; | ||
90 | } | ||
91 | |||
92 | static void tvc_disable(struct omap_dss_device *dssdev) | ||
93 | { | ||
94 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
95 | struct omap_dss_device *in = ddata->in; | ||
96 | |||
97 | dev_dbg(ddata->dev, "disable\n"); | ||
98 | |||
99 | if (!omapdss_device_is_enabled(dssdev)) | ||
100 | return; | ||
101 | |||
102 | in->ops.atv->disable(in); | ||
103 | |||
104 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; | ||
105 | } | ||
106 | |||
107 | static void tvc_set_timings(struct omap_dss_device *dssdev, | ||
108 | struct omap_video_timings *timings) | ||
109 | { | ||
110 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
111 | struct omap_dss_device *in = ddata->in; | ||
112 | |||
113 | ddata->timings = *timings; | ||
114 | dssdev->panel.timings = *timings; | ||
115 | |||
116 | in->ops.atv->set_timings(in, timings); | ||
117 | } | ||
118 | |||
119 | static void tvc_get_timings(struct omap_dss_device *dssdev, | ||
120 | struct omap_video_timings *timings) | ||
121 | { | ||
122 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
123 | |||
124 | *timings = ddata->timings; | ||
125 | } | ||
126 | |||
127 | static int tvc_check_timings(struct omap_dss_device *dssdev, | ||
128 | struct omap_video_timings *timings) | ||
129 | { | ||
130 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
131 | struct omap_dss_device *in = ddata->in; | ||
132 | |||
133 | return in->ops.atv->check_timings(in, timings); | ||
134 | } | ||
135 | |||
136 | static u32 tvc_get_wss(struct omap_dss_device *dssdev) | ||
137 | { | ||
138 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
139 | struct omap_dss_device *in = ddata->in; | ||
140 | |||
141 | return in->ops.atv->get_wss(in); | ||
142 | } | ||
143 | |||
144 | static int tvc_set_wss(struct omap_dss_device *dssdev, u32 wss) | ||
145 | { | ||
146 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
147 | struct omap_dss_device *in = ddata->in; | ||
148 | |||
149 | return in->ops.atv->set_wss(in, wss); | ||
150 | } | ||
151 | |||
152 | static struct omap_dss_driver tvc_driver = { | ||
153 | .connect = tvc_connect, | ||
154 | .disconnect = tvc_disconnect, | ||
155 | |||
156 | .enable = tvc_enable, | ||
157 | .disable = tvc_disable, | ||
158 | |||
159 | .set_timings = tvc_set_timings, | ||
160 | .get_timings = tvc_get_timings, | ||
161 | .check_timings = tvc_check_timings, | ||
162 | |||
163 | .get_resolution = omapdss_default_get_resolution, | ||
164 | |||
165 | .get_wss = tvc_get_wss, | ||
166 | .set_wss = tvc_set_wss, | ||
167 | }; | ||
168 | |||
169 | static int tvc_probe_pdata(struct platform_device *pdev) | ||
170 | { | ||
171 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
172 | struct connector_atv_platform_data *pdata; | ||
173 | struct omap_dss_device *in, *dssdev; | ||
174 | |||
175 | pdata = dev_get_platdata(&pdev->dev); | ||
176 | |||
177 | in = omap_dss_find_output(pdata->source); | ||
178 | if (in == NULL) { | ||
179 | dev_err(&pdev->dev, "Failed to find video source\n"); | ||
180 | return -ENODEV; | ||
181 | } | ||
182 | |||
183 | ddata->in = in; | ||
184 | |||
185 | ddata->connector_type = pdata->connector_type; | ||
186 | ddata->invert_polarity = ddata->invert_polarity; | ||
187 | |||
188 | dssdev = &ddata->dssdev; | ||
189 | dssdev->name = pdata->name; | ||
190 | |||
191 | return 0; | ||
192 | } | ||
193 | |||
194 | static int tvc_probe(struct platform_device *pdev) | ||
195 | { | ||
196 | struct panel_drv_data *ddata; | ||
197 | struct omap_dss_device *dssdev; | ||
198 | int r; | ||
199 | |||
200 | ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); | ||
201 | if (!ddata) | ||
202 | return -ENOMEM; | ||
203 | |||
204 | platform_set_drvdata(pdev, ddata); | ||
205 | ddata->dev = &pdev->dev; | ||
206 | |||
207 | if (dev_get_platdata(&pdev->dev)) { | ||
208 | r = tvc_probe_pdata(pdev); | ||
209 | if (r) | ||
210 | return r; | ||
211 | } else { | ||
212 | return -ENODEV; | ||
213 | } | ||
214 | |||
215 | ddata->timings = omap_dss_pal_timings; | ||
216 | |||
217 | dssdev = &ddata->dssdev; | ||
218 | dssdev->driver = &tvc_driver; | ||
219 | dssdev->dev = &pdev->dev; | ||
220 | dssdev->type = OMAP_DISPLAY_TYPE_VENC; | ||
221 | dssdev->owner = THIS_MODULE; | ||
222 | dssdev->panel.timings = omap_dss_pal_timings; | ||
223 | |||
224 | r = omapdss_register_display(dssdev); | ||
225 | if (r) { | ||
226 | dev_err(&pdev->dev, "Failed to register panel\n"); | ||
227 | goto err_reg; | ||
228 | } | ||
229 | |||
230 | return 0; | ||
231 | err_reg: | ||
232 | omap_dss_put_device(ddata->in); | ||
233 | return r; | ||
234 | } | ||
235 | |||
236 | static int __exit tvc_remove(struct platform_device *pdev) | ||
237 | { | ||
238 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
239 | struct omap_dss_device *dssdev = &ddata->dssdev; | ||
240 | struct omap_dss_device *in = ddata->in; | ||
241 | |||
242 | omapdss_unregister_display(&ddata->dssdev); | ||
243 | |||
244 | tvc_disable(dssdev); | ||
245 | tvc_disconnect(dssdev); | ||
246 | |||
247 | omap_dss_put_device(in); | ||
248 | |||
249 | return 0; | ||
250 | } | ||
251 | |||
252 | static struct platform_driver tvc_connector_driver = { | ||
253 | .probe = tvc_probe, | ||
254 | .remove = __exit_p(tvc_remove), | ||
255 | .driver = { | ||
256 | .name = "connector-analog-tv", | ||
257 | .owner = THIS_MODULE, | ||
258 | }, | ||
259 | }; | ||
260 | |||
261 | module_platform_driver(tvc_connector_driver); | ||
262 | |||
263 | MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); | ||
264 | MODULE_DESCRIPTION("Analog TV Connector driver"); | ||
265 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/omap2/displays-new/connector-dvi.c b/drivers/video/omap2/displays-new/connector-dvi.c new file mode 100644 index 000000000000..bc5f8ceda371 --- /dev/null +++ b/drivers/video/omap2/displays-new/connector-dvi.c | |||
@@ -0,0 +1,351 @@ | |||
1 | /* | ||
2 | * Generic DVI Connector driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Texas Instruments | ||
5 | * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License version 2 as published by | ||
9 | * the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/i2c.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/slab.h> | ||
16 | |||
17 | #include <drm/drm_edid.h> | ||
18 | |||
19 | #include <video/omapdss.h> | ||
20 | #include <video/omap-panel-data.h> | ||
21 | |||
22 | static const struct omap_video_timings dvic_default_timings = { | ||
23 | .x_res = 640, | ||
24 | .y_res = 480, | ||
25 | |||
26 | .pixel_clock = 23500, | ||
27 | |||
28 | .hfp = 48, | ||
29 | .hsw = 32, | ||
30 | .hbp = 80, | ||
31 | |||
32 | .vfp = 3, | ||
33 | .vsw = 4, | ||
34 | .vbp = 7, | ||
35 | |||
36 | .vsync_level = OMAPDSS_SIG_ACTIVE_HIGH, | ||
37 | .hsync_level = OMAPDSS_SIG_ACTIVE_HIGH, | ||
38 | .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, | ||
39 | .de_level = OMAPDSS_SIG_ACTIVE_HIGH, | ||
40 | .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, | ||
41 | }; | ||
42 | |||
43 | struct panel_drv_data { | ||
44 | struct omap_dss_device dssdev; | ||
45 | struct omap_dss_device *in; | ||
46 | |||
47 | struct omap_video_timings timings; | ||
48 | |||
49 | struct i2c_adapter *i2c_adapter; | ||
50 | }; | ||
51 | |||
52 | #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) | ||
53 | |||
54 | static int dvic_connect(struct omap_dss_device *dssdev) | ||
55 | { | ||
56 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
57 | struct omap_dss_device *in = ddata->in; | ||
58 | int r; | ||
59 | |||
60 | if (omapdss_device_is_connected(dssdev)) | ||
61 | return 0; | ||
62 | |||
63 | r = in->ops.dvi->connect(in, dssdev); | ||
64 | if (r) | ||
65 | return r; | ||
66 | |||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | static void dvic_disconnect(struct omap_dss_device *dssdev) | ||
71 | { | ||
72 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
73 | struct omap_dss_device *in = ddata->in; | ||
74 | |||
75 | if (!omapdss_device_is_connected(dssdev)) | ||
76 | return; | ||
77 | |||
78 | in->ops.dvi->disconnect(in, dssdev); | ||
79 | } | ||
80 | |||
81 | static int dvic_enable(struct omap_dss_device *dssdev) | ||
82 | { | ||
83 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
84 | struct omap_dss_device *in = ddata->in; | ||
85 | int r; | ||
86 | |||
87 | if (!omapdss_device_is_connected(dssdev)) | ||
88 | return -ENODEV; | ||
89 | |||
90 | if (omapdss_device_is_enabled(dssdev)) | ||
91 | return 0; | ||
92 | |||
93 | in->ops.dvi->set_timings(in, &ddata->timings); | ||
94 | |||
95 | r = in->ops.dvi->enable(in); | ||
96 | if (r) | ||
97 | return r; | ||
98 | |||
99 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | ||
100 | |||
101 | return 0; | ||
102 | } | ||
103 | |||
104 | static void dvic_disable(struct omap_dss_device *dssdev) | ||
105 | { | ||
106 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
107 | struct omap_dss_device *in = ddata->in; | ||
108 | |||
109 | if (!omapdss_device_is_enabled(dssdev)) | ||
110 | return; | ||
111 | |||
112 | in->ops.dvi->disable(in); | ||
113 | |||
114 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; | ||
115 | } | ||
116 | |||
117 | static void dvic_set_timings(struct omap_dss_device *dssdev, | ||
118 | struct omap_video_timings *timings) | ||
119 | { | ||
120 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
121 | struct omap_dss_device *in = ddata->in; | ||
122 | |||
123 | ddata->timings = *timings; | ||
124 | dssdev->panel.timings = *timings; | ||
125 | |||
126 | in->ops.dvi->set_timings(in, timings); | ||
127 | } | ||
128 | |||
129 | static void dvic_get_timings(struct omap_dss_device *dssdev, | ||
130 | struct omap_video_timings *timings) | ||
131 | { | ||
132 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
133 | |||
134 | *timings = ddata->timings; | ||
135 | } | ||
136 | |||
137 | static int dvic_check_timings(struct omap_dss_device *dssdev, | ||
138 | struct omap_video_timings *timings) | ||
139 | { | ||
140 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
141 | struct omap_dss_device *in = ddata->in; | ||
142 | |||
143 | return in->ops.dvi->check_timings(in, timings); | ||
144 | } | ||
145 | |||
146 | static int dvic_ddc_read(struct i2c_adapter *adapter, | ||
147 | unsigned char *buf, u16 count, u8 offset) | ||
148 | { | ||
149 | int r, retries; | ||
150 | |||
151 | for (retries = 3; retries > 0; retries--) { | ||
152 | struct i2c_msg msgs[] = { | ||
153 | { | ||
154 | .addr = DDC_ADDR, | ||
155 | .flags = 0, | ||
156 | .len = 1, | ||
157 | .buf = &offset, | ||
158 | }, { | ||
159 | .addr = DDC_ADDR, | ||
160 | .flags = I2C_M_RD, | ||
161 | .len = count, | ||
162 | .buf = buf, | ||
163 | } | ||
164 | }; | ||
165 | |||
166 | r = i2c_transfer(adapter, msgs, 2); | ||
167 | if (r == 2) | ||
168 | return 0; | ||
169 | |||
170 | if (r != -EAGAIN) | ||
171 | break; | ||
172 | } | ||
173 | |||
174 | return r < 0 ? r : -EIO; | ||
175 | } | ||
176 | |||
177 | static int dvic_read_edid(struct omap_dss_device *dssdev, | ||
178 | u8 *edid, int len) | ||
179 | { | ||
180 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
181 | int r, l, bytes_read; | ||
182 | |||
183 | if (!ddata->i2c_adapter) | ||
184 | return -ENODEV; | ||
185 | |||
186 | l = min(EDID_LENGTH, len); | ||
187 | r = dvic_ddc_read(ddata->i2c_adapter, edid, l, 0); | ||
188 | if (r) | ||
189 | return r; | ||
190 | |||
191 | bytes_read = l; | ||
192 | |||
193 | /* if there are extensions, read second block */ | ||
194 | if (len > EDID_LENGTH && edid[0x7e] > 0) { | ||
195 | l = min(EDID_LENGTH, len - EDID_LENGTH); | ||
196 | |||
197 | r = dvic_ddc_read(ddata->i2c_adapter, edid + EDID_LENGTH, | ||
198 | l, EDID_LENGTH); | ||
199 | if (r) | ||
200 | return r; | ||
201 | |||
202 | bytes_read += l; | ||
203 | } | ||
204 | |||
205 | return bytes_read; | ||
206 | } | ||
207 | |||
208 | static bool dvic_detect(struct omap_dss_device *dssdev) | ||
209 | { | ||
210 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
211 | unsigned char out; | ||
212 | int r; | ||
213 | |||
214 | if (!ddata->i2c_adapter) | ||
215 | return true; | ||
216 | |||
217 | r = dvic_ddc_read(ddata->i2c_adapter, &out, 1, 0); | ||
218 | |||
219 | return r == 0; | ||
220 | } | ||
221 | |||
222 | static struct omap_dss_driver dvic_driver = { | ||
223 | .connect = dvic_connect, | ||
224 | .disconnect = dvic_disconnect, | ||
225 | |||
226 | .enable = dvic_enable, | ||
227 | .disable = dvic_disable, | ||
228 | |||
229 | .set_timings = dvic_set_timings, | ||
230 | .get_timings = dvic_get_timings, | ||
231 | .check_timings = dvic_check_timings, | ||
232 | |||
233 | .get_resolution = omapdss_default_get_resolution, | ||
234 | |||
235 | .read_edid = dvic_read_edid, | ||
236 | .detect = dvic_detect, | ||
237 | }; | ||
238 | |||
239 | static int dvic_probe_pdata(struct platform_device *pdev) | ||
240 | { | ||
241 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
242 | struct connector_dvi_platform_data *pdata; | ||
243 | struct omap_dss_device *in, *dssdev; | ||
244 | int i2c_bus_num; | ||
245 | |||
246 | pdata = dev_get_platdata(&pdev->dev); | ||
247 | i2c_bus_num = pdata->i2c_bus_num; | ||
248 | |||
249 | if (i2c_bus_num != -1) { | ||
250 | struct i2c_adapter *adapter; | ||
251 | |||
252 | adapter = i2c_get_adapter(i2c_bus_num); | ||
253 | if (!adapter) { | ||
254 | dev_err(&pdev->dev, | ||
255 | "Failed to get I2C adapter, bus %d\n", | ||
256 | i2c_bus_num); | ||
257 | return -EPROBE_DEFER; | ||
258 | } | ||
259 | |||
260 | ddata->i2c_adapter = adapter; | ||
261 | } | ||
262 | |||
263 | in = omap_dss_find_output(pdata->source); | ||
264 | if (in == NULL) { | ||
265 | dev_err(&pdev->dev, "Failed to find video source\n"); | ||
266 | return -ENODEV; | ||
267 | } | ||
268 | |||
269 | ddata->in = in; | ||
270 | |||
271 | dssdev = &ddata->dssdev; | ||
272 | dssdev->name = pdata->name; | ||
273 | |||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | static int dvic_probe(struct platform_device *pdev) | ||
278 | { | ||
279 | struct panel_drv_data *ddata; | ||
280 | struct omap_dss_device *dssdev; | ||
281 | int r; | ||
282 | |||
283 | ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); | ||
284 | if (!ddata) | ||
285 | return -ENOMEM; | ||
286 | |||
287 | platform_set_drvdata(pdev, ddata); | ||
288 | |||
289 | if (dev_get_platdata(&pdev->dev)) { | ||
290 | r = dvic_probe_pdata(pdev); | ||
291 | if (r) | ||
292 | return r; | ||
293 | } else { | ||
294 | return -ENODEV; | ||
295 | } | ||
296 | |||
297 | ddata->timings = dvic_default_timings; | ||
298 | |||
299 | dssdev = &ddata->dssdev; | ||
300 | dssdev->driver = &dvic_driver; | ||
301 | dssdev->dev = &pdev->dev; | ||
302 | dssdev->type = OMAP_DISPLAY_TYPE_DVI; | ||
303 | dssdev->owner = THIS_MODULE; | ||
304 | dssdev->panel.timings = dvic_default_timings; | ||
305 | |||
306 | r = omapdss_register_display(dssdev); | ||
307 | if (r) { | ||
308 | dev_err(&pdev->dev, "Failed to register panel\n"); | ||
309 | goto err_reg; | ||
310 | } | ||
311 | |||
312 | return 0; | ||
313 | |||
314 | err_reg: | ||
315 | omap_dss_put_device(ddata->in); | ||
316 | return r; | ||
317 | } | ||
318 | |||
319 | static int __exit dvic_remove(struct platform_device *pdev) | ||
320 | { | ||
321 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
322 | struct omap_dss_device *dssdev = &ddata->dssdev; | ||
323 | struct omap_dss_device *in = ddata->in; | ||
324 | |||
325 | omapdss_unregister_display(&ddata->dssdev); | ||
326 | |||
327 | dvic_disable(dssdev); | ||
328 | dvic_disconnect(dssdev); | ||
329 | |||
330 | omap_dss_put_device(in); | ||
331 | |||
332 | if (ddata->i2c_adapter) | ||
333 | i2c_put_adapter(ddata->i2c_adapter); | ||
334 | |||
335 | return 0; | ||
336 | } | ||
337 | |||
338 | static struct platform_driver dvi_connector_driver = { | ||
339 | .probe = dvic_probe, | ||
340 | .remove = __exit_p(dvic_remove), | ||
341 | .driver = { | ||
342 | .name = "connector-dvi", | ||
343 | .owner = THIS_MODULE, | ||
344 | }, | ||
345 | }; | ||
346 | |||
347 | module_platform_driver(dvi_connector_driver); | ||
348 | |||
349 | MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); | ||
350 | MODULE_DESCRIPTION("Generic DVI Connector driver"); | ||
351 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/omap2/displays-new/connector-hdmi.c b/drivers/video/omap2/displays-new/connector-hdmi.c new file mode 100644 index 000000000000..c5826716d6ab --- /dev/null +++ b/drivers/video/omap2/displays-new/connector-hdmi.c | |||
@@ -0,0 +1,375 @@ | |||
1 | /* | ||
2 | * HDMI Connector driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Texas Instruments | ||
5 | * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License version 2 as published by | ||
9 | * the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/slab.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | |||
16 | #include <drm/drm_edid.h> | ||
17 | |||
18 | #include <video/omapdss.h> | ||
19 | #include <video/omap-panel-data.h> | ||
20 | |||
21 | static const struct omap_video_timings hdmic_default_timings = { | ||
22 | .x_res = 640, | ||
23 | .y_res = 480, | ||
24 | .pixel_clock = 25175, | ||
25 | .hsw = 96, | ||
26 | .hfp = 16, | ||
27 | .hbp = 48, | ||
28 | .vsw = 2, | ||
29 | .vfp = 11, | ||
30 | .vbp = 31, | ||
31 | |||
32 | .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, | ||
33 | .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, | ||
34 | |||
35 | .interlace = false, | ||
36 | }; | ||
37 | |||
38 | struct panel_drv_data { | ||
39 | struct omap_dss_device dssdev; | ||
40 | struct omap_dss_device *in; | ||
41 | |||
42 | struct device *dev; | ||
43 | |||
44 | struct omap_video_timings timings; | ||
45 | }; | ||
46 | |||
47 | #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) | ||
48 | |||
49 | static int hdmic_connect(struct omap_dss_device *dssdev) | ||
50 | { | ||
51 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
52 | struct omap_dss_device *in = ddata->in; | ||
53 | int r; | ||
54 | |||
55 | dev_dbg(ddata->dev, "connect\n"); | ||
56 | |||
57 | if (omapdss_device_is_connected(dssdev)) | ||
58 | return 0; | ||
59 | |||
60 | r = in->ops.hdmi->connect(in, dssdev); | ||
61 | if (r) | ||
62 | return r; | ||
63 | |||
64 | return 0; | ||
65 | } | ||
66 | |||
67 | static void hdmic_disconnect(struct omap_dss_device *dssdev) | ||
68 | { | ||
69 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
70 | struct omap_dss_device *in = ddata->in; | ||
71 | |||
72 | dev_dbg(ddata->dev, "disconnect\n"); | ||
73 | |||
74 | if (!omapdss_device_is_connected(dssdev)) | ||
75 | return; | ||
76 | |||
77 | in->ops.hdmi->disconnect(in, dssdev); | ||
78 | } | ||
79 | |||
80 | static int hdmic_enable(struct omap_dss_device *dssdev) | ||
81 | { | ||
82 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
83 | struct omap_dss_device *in = ddata->in; | ||
84 | int r; | ||
85 | |||
86 | dev_dbg(ddata->dev, "enable\n"); | ||
87 | |||
88 | if (!omapdss_device_is_connected(dssdev)) | ||
89 | return -ENODEV; | ||
90 | |||
91 | if (omapdss_device_is_enabled(dssdev)) | ||
92 | return 0; | ||
93 | |||
94 | in->ops.hdmi->set_timings(in, &ddata->timings); | ||
95 | |||
96 | r = in->ops.hdmi->enable(in); | ||
97 | if (r) | ||
98 | return r; | ||
99 | |||
100 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | ||
101 | |||
102 | return r; | ||
103 | } | ||
104 | |||
105 | static void hdmic_disable(struct omap_dss_device *dssdev) | ||
106 | { | ||
107 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
108 | struct omap_dss_device *in = ddata->in; | ||
109 | |||
110 | dev_dbg(ddata->dev, "disable\n"); | ||
111 | |||
112 | if (!omapdss_device_is_enabled(dssdev)) | ||
113 | return; | ||
114 | |||
115 | in->ops.hdmi->disable(in); | ||
116 | |||
117 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; | ||
118 | } | ||
119 | |||
120 | static void hdmic_set_timings(struct omap_dss_device *dssdev, | ||
121 | struct omap_video_timings *timings) | ||
122 | { | ||
123 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
124 | struct omap_dss_device *in = ddata->in; | ||
125 | |||
126 | ddata->timings = *timings; | ||
127 | dssdev->panel.timings = *timings; | ||
128 | |||
129 | in->ops.hdmi->set_timings(in, timings); | ||
130 | } | ||
131 | |||
132 | static void hdmic_get_timings(struct omap_dss_device *dssdev, | ||
133 | struct omap_video_timings *timings) | ||
134 | { | ||
135 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
136 | |||
137 | *timings = ddata->timings; | ||
138 | } | ||
139 | |||
140 | static int hdmic_check_timings(struct omap_dss_device *dssdev, | ||
141 | struct omap_video_timings *timings) | ||
142 | { | ||
143 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
144 | struct omap_dss_device *in = ddata->in; | ||
145 | |||
146 | return in->ops.hdmi->check_timings(in, timings); | ||
147 | } | ||
148 | |||
149 | static int hdmic_read_edid(struct omap_dss_device *dssdev, | ||
150 | u8 *edid, int len) | ||
151 | { | ||
152 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
153 | struct omap_dss_device *in = ddata->in; | ||
154 | |||
155 | return in->ops.hdmi->read_edid(in, edid, len); | ||
156 | } | ||
157 | |||
158 | static bool hdmic_detect(struct omap_dss_device *dssdev) | ||
159 | { | ||
160 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
161 | struct omap_dss_device *in = ddata->in; | ||
162 | |||
163 | return in->ops.hdmi->detect(in); | ||
164 | } | ||
165 | |||
166 | static int hdmic_audio_enable(struct omap_dss_device *dssdev) | ||
167 | { | ||
168 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
169 | struct omap_dss_device *in = ddata->in; | ||
170 | int r; | ||
171 | |||
172 | /* enable audio only if the display is active */ | ||
173 | if (!omapdss_device_is_enabled(dssdev)) | ||
174 | return -EPERM; | ||
175 | |||
176 | r = in->ops.hdmi->audio_enable(in); | ||
177 | if (r) | ||
178 | return r; | ||
179 | |||
180 | dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED; | ||
181 | |||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | static void hdmic_audio_disable(struct omap_dss_device *dssdev) | ||
186 | { | ||
187 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
188 | struct omap_dss_device *in = ddata->in; | ||
189 | |||
190 | in->ops.hdmi->audio_disable(in); | ||
191 | |||
192 | dssdev->audio_state = OMAP_DSS_AUDIO_DISABLED; | ||
193 | } | ||
194 | |||
195 | static int hdmic_audio_start(struct omap_dss_device *dssdev) | ||
196 | { | ||
197 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
198 | struct omap_dss_device *in = ddata->in; | ||
199 | int r; | ||
200 | |||
201 | /* | ||
202 | * No need to check the panel state. It was checked when trasitioning | ||
203 | * to AUDIO_ENABLED. | ||
204 | */ | ||
205 | if (dssdev->audio_state != OMAP_DSS_AUDIO_ENABLED) | ||
206 | return -EPERM; | ||
207 | |||
208 | r = in->ops.hdmi->audio_start(in); | ||
209 | if (r) | ||
210 | return r; | ||
211 | |||
212 | dssdev->audio_state = OMAP_DSS_AUDIO_PLAYING; | ||
213 | |||
214 | return 0; | ||
215 | } | ||
216 | |||
217 | static void hdmic_audio_stop(struct omap_dss_device *dssdev) | ||
218 | { | ||
219 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
220 | struct omap_dss_device *in = ddata->in; | ||
221 | |||
222 | in->ops.hdmi->audio_stop(in); | ||
223 | |||
224 | dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED; | ||
225 | } | ||
226 | |||
227 | static bool hdmic_audio_supported(struct omap_dss_device *dssdev) | ||
228 | { | ||
229 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
230 | struct omap_dss_device *in = ddata->in; | ||
231 | |||
232 | if (!omapdss_device_is_enabled(dssdev)) | ||
233 | return false; | ||
234 | |||
235 | return in->ops.hdmi->audio_supported(in); | ||
236 | } | ||
237 | |||
238 | static int hdmic_audio_config(struct omap_dss_device *dssdev, | ||
239 | struct omap_dss_audio *audio) | ||
240 | { | ||
241 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
242 | struct omap_dss_device *in = ddata->in; | ||
243 | int r; | ||
244 | |||
245 | /* config audio only if the display is active */ | ||
246 | if (!omapdss_device_is_enabled(dssdev)) | ||
247 | return -EPERM; | ||
248 | |||
249 | r = in->ops.hdmi->audio_config(in, audio); | ||
250 | if (r) | ||
251 | return r; | ||
252 | |||
253 | dssdev->audio_state = OMAP_DSS_AUDIO_CONFIGURED; | ||
254 | |||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | static struct omap_dss_driver hdmic_driver = { | ||
259 | .connect = hdmic_connect, | ||
260 | .disconnect = hdmic_disconnect, | ||
261 | |||
262 | .enable = hdmic_enable, | ||
263 | .disable = hdmic_disable, | ||
264 | |||
265 | .set_timings = hdmic_set_timings, | ||
266 | .get_timings = hdmic_get_timings, | ||
267 | .check_timings = hdmic_check_timings, | ||
268 | |||
269 | .get_resolution = omapdss_default_get_resolution, | ||
270 | |||
271 | .read_edid = hdmic_read_edid, | ||
272 | .detect = hdmic_detect, | ||
273 | |||
274 | .audio_enable = hdmic_audio_enable, | ||
275 | .audio_disable = hdmic_audio_disable, | ||
276 | .audio_start = hdmic_audio_start, | ||
277 | .audio_stop = hdmic_audio_stop, | ||
278 | .audio_supported = hdmic_audio_supported, | ||
279 | .audio_config = hdmic_audio_config, | ||
280 | }; | ||
281 | |||
282 | static int hdmic_probe_pdata(struct platform_device *pdev) | ||
283 | { | ||
284 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
285 | struct connector_hdmi_platform_data *pdata; | ||
286 | struct omap_dss_device *in, *dssdev; | ||
287 | |||
288 | pdata = dev_get_platdata(&pdev->dev); | ||
289 | |||
290 | in = omap_dss_find_output(pdata->source); | ||
291 | if (in == NULL) { | ||
292 | dev_err(&pdev->dev, "Failed to find video source\n"); | ||
293 | return -ENODEV; | ||
294 | } | ||
295 | |||
296 | ddata->in = in; | ||
297 | |||
298 | dssdev = &ddata->dssdev; | ||
299 | dssdev->name = pdata->name; | ||
300 | |||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | static int hdmic_probe(struct platform_device *pdev) | ||
305 | { | ||
306 | struct panel_drv_data *ddata; | ||
307 | struct omap_dss_device *dssdev; | ||
308 | int r; | ||
309 | |||
310 | ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); | ||
311 | if (!ddata) | ||
312 | return -ENOMEM; | ||
313 | |||
314 | platform_set_drvdata(pdev, ddata); | ||
315 | ddata->dev = &pdev->dev; | ||
316 | |||
317 | if (dev_get_platdata(&pdev->dev)) { | ||
318 | r = hdmic_probe_pdata(pdev); | ||
319 | if (r) | ||
320 | return r; | ||
321 | } else { | ||
322 | return -ENODEV; | ||
323 | } | ||
324 | |||
325 | ddata->timings = hdmic_default_timings; | ||
326 | |||
327 | dssdev = &ddata->dssdev; | ||
328 | dssdev->driver = &hdmic_driver; | ||
329 | dssdev->dev = &pdev->dev; | ||
330 | dssdev->type = OMAP_DISPLAY_TYPE_HDMI; | ||
331 | dssdev->owner = THIS_MODULE; | ||
332 | dssdev->panel.timings = hdmic_default_timings; | ||
333 | |||
334 | r = omapdss_register_display(dssdev); | ||
335 | if (r) { | ||
336 | dev_err(&pdev->dev, "Failed to register panel\n"); | ||
337 | goto err_reg; | ||
338 | } | ||
339 | |||
340 | return 0; | ||
341 | err_reg: | ||
342 | omap_dss_put_device(ddata->in); | ||
343 | return r; | ||
344 | } | ||
345 | |||
346 | static int __exit hdmic_remove(struct platform_device *pdev) | ||
347 | { | ||
348 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
349 | struct omap_dss_device *dssdev = &ddata->dssdev; | ||
350 | struct omap_dss_device *in = ddata->in; | ||
351 | |||
352 | omapdss_unregister_display(&ddata->dssdev); | ||
353 | |||
354 | hdmic_disable(dssdev); | ||
355 | hdmic_disconnect(dssdev); | ||
356 | |||
357 | omap_dss_put_device(in); | ||
358 | |||
359 | return 0; | ||
360 | } | ||
361 | |||
362 | static struct platform_driver hdmi_connector_driver = { | ||
363 | .probe = hdmic_probe, | ||
364 | .remove = __exit_p(hdmic_remove), | ||
365 | .driver = { | ||
366 | .name = "connector-hdmi", | ||
367 | .owner = THIS_MODULE, | ||
368 | }, | ||
369 | }; | ||
370 | |||
371 | module_platform_driver(hdmi_connector_driver); | ||
372 | |||
373 | MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); | ||
374 | MODULE_DESCRIPTION("HDMI Connector driver"); | ||
375 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/omap2/displays-new/encoder-tfp410.c b/drivers/video/omap2/displays-new/encoder-tfp410.c new file mode 100644 index 000000000000..a04f65856d6b --- /dev/null +++ b/drivers/video/omap2/displays-new/encoder-tfp410.c | |||
@@ -0,0 +1,267 @@ | |||
1 | /* | ||
2 | * TFP410 DPI-to-DVI encoder driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Texas Instruments | ||
5 | * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License version 2 as published by | ||
9 | * the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/gpio.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/slab.h> | ||
16 | |||
17 | #include <video/omapdss.h> | ||
18 | #include <video/omap-panel-data.h> | ||
19 | |||
20 | struct panel_drv_data { | ||
21 | struct omap_dss_device dssdev; | ||
22 | struct omap_dss_device *in; | ||
23 | |||
24 | int pd_gpio; | ||
25 | int data_lines; | ||
26 | |||
27 | struct omap_video_timings timings; | ||
28 | }; | ||
29 | |||
30 | #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) | ||
31 | |||
32 | static int tfp410_connect(struct omap_dss_device *dssdev, | ||
33 | struct omap_dss_device *dst) | ||
34 | { | ||
35 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
36 | struct omap_dss_device *in = ddata->in; | ||
37 | int r; | ||
38 | |||
39 | if (omapdss_device_is_connected(dssdev)) | ||
40 | return -EBUSY; | ||
41 | |||
42 | r = in->ops.dpi->connect(in, dssdev); | ||
43 | if (r) | ||
44 | return r; | ||
45 | |||
46 | dst->output = dssdev; | ||
47 | dssdev->device = dst; | ||
48 | |||
49 | return 0; | ||
50 | } | ||
51 | |||
52 | static void tfp410_disconnect(struct omap_dss_device *dssdev, | ||
53 | struct omap_dss_device *dst) | ||
54 | { | ||
55 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
56 | struct omap_dss_device *in = ddata->in; | ||
57 | |||
58 | WARN_ON(!omapdss_device_is_connected(dssdev)); | ||
59 | if (!omapdss_device_is_connected(dssdev)) | ||
60 | return; | ||
61 | |||
62 | WARN_ON(dst != dssdev->device); | ||
63 | if (dst != dssdev->device) | ||
64 | return; | ||
65 | |||
66 | dst->output = NULL; | ||
67 | dssdev->device = NULL; | ||
68 | |||
69 | in->ops.dpi->disconnect(in, &ddata->dssdev); | ||
70 | } | ||
71 | |||
72 | static int tfp410_enable(struct omap_dss_device *dssdev) | ||
73 | { | ||
74 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
75 | struct omap_dss_device *in = ddata->in; | ||
76 | int r; | ||
77 | |||
78 | if (!omapdss_device_is_connected(dssdev)) | ||
79 | return -ENODEV; | ||
80 | |||
81 | if (omapdss_device_is_enabled(dssdev)) | ||
82 | return 0; | ||
83 | |||
84 | in->ops.dpi->set_timings(in, &ddata->timings); | ||
85 | in->ops.dpi->set_data_lines(in, ddata->data_lines); | ||
86 | |||
87 | r = in->ops.dpi->enable(in); | ||
88 | if (r) | ||
89 | return r; | ||
90 | |||
91 | if (gpio_is_valid(ddata->pd_gpio)) | ||
92 | gpio_set_value_cansleep(ddata->pd_gpio, 1); | ||
93 | |||
94 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | ||
95 | |||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static void tfp410_disable(struct omap_dss_device *dssdev) | ||
100 | { | ||
101 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
102 | struct omap_dss_device *in = ddata->in; | ||
103 | |||
104 | if (!omapdss_device_is_enabled(dssdev)) | ||
105 | return; | ||
106 | |||
107 | if (gpio_is_valid(ddata->pd_gpio)) | ||
108 | gpio_set_value_cansleep(ddata->pd_gpio, 0); | ||
109 | |||
110 | in->ops.dpi->disable(in); | ||
111 | |||
112 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; | ||
113 | } | ||
114 | |||
115 | static void tfp410_set_timings(struct omap_dss_device *dssdev, | ||
116 | struct omap_video_timings *timings) | ||
117 | { | ||
118 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
119 | struct omap_dss_device *in = ddata->in; | ||
120 | |||
121 | ddata->timings = *timings; | ||
122 | dssdev->panel.timings = *timings; | ||
123 | |||
124 | in->ops.dpi->set_timings(in, timings); | ||
125 | } | ||
126 | |||
127 | static void tfp410_get_timings(struct omap_dss_device *dssdev, | ||
128 | struct omap_video_timings *timings) | ||
129 | { | ||
130 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
131 | |||
132 | *timings = ddata->timings; | ||
133 | } | ||
134 | |||
135 | static int tfp410_check_timings(struct omap_dss_device *dssdev, | ||
136 | struct omap_video_timings *timings) | ||
137 | { | ||
138 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
139 | struct omap_dss_device *in = ddata->in; | ||
140 | |||
141 | return in->ops.dpi->check_timings(in, timings); | ||
142 | } | ||
143 | |||
144 | static const struct omapdss_dvi_ops tfp410_dvi_ops = { | ||
145 | .connect = tfp410_connect, | ||
146 | .disconnect = tfp410_disconnect, | ||
147 | |||
148 | .enable = tfp410_enable, | ||
149 | .disable = tfp410_disable, | ||
150 | |||
151 | .check_timings = tfp410_check_timings, | ||
152 | .set_timings = tfp410_set_timings, | ||
153 | .get_timings = tfp410_get_timings, | ||
154 | }; | ||
155 | |||
156 | static int tfp410_probe_pdata(struct platform_device *pdev) | ||
157 | { | ||
158 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
159 | struct encoder_tfp410_platform_data *pdata; | ||
160 | struct omap_dss_device *dssdev, *in; | ||
161 | |||
162 | pdata = dev_get_platdata(&pdev->dev); | ||
163 | |||
164 | ddata->pd_gpio = pdata->power_down_gpio; | ||
165 | |||
166 | ddata->data_lines = pdata->data_lines; | ||
167 | |||
168 | in = omap_dss_find_output(pdata->source); | ||
169 | if (in == NULL) { | ||
170 | dev_err(&pdev->dev, "Failed to find video source\n"); | ||
171 | return -ENODEV; | ||
172 | } | ||
173 | |||
174 | ddata->in = in; | ||
175 | |||
176 | dssdev = &ddata->dssdev; | ||
177 | dssdev->name = pdata->name; | ||
178 | |||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | static int tfp410_probe(struct platform_device *pdev) | ||
183 | { | ||
184 | struct panel_drv_data *ddata; | ||
185 | struct omap_dss_device *dssdev; | ||
186 | int r; | ||
187 | |||
188 | ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); | ||
189 | if (!ddata) | ||
190 | return -ENOMEM; | ||
191 | |||
192 | platform_set_drvdata(pdev, ddata); | ||
193 | |||
194 | if (dev_get_platdata(&pdev->dev)) { | ||
195 | r = tfp410_probe_pdata(pdev); | ||
196 | if (r) | ||
197 | return r; | ||
198 | } else { | ||
199 | return -ENODEV; | ||
200 | } | ||
201 | |||
202 | if (gpio_is_valid(ddata->pd_gpio)) { | ||
203 | r = devm_gpio_request_one(&pdev->dev, ddata->pd_gpio, | ||
204 | GPIOF_OUT_INIT_LOW, "tfp410 PD"); | ||
205 | if (r) { | ||
206 | dev_err(&pdev->dev, "Failed to request PD GPIO %d\n", | ||
207 | ddata->pd_gpio); | ||
208 | goto err_gpio; | ||
209 | } | ||
210 | } | ||
211 | |||
212 | dssdev = &ddata->dssdev; | ||
213 | dssdev->ops.dvi = &tfp410_dvi_ops; | ||
214 | dssdev->dev = &pdev->dev; | ||
215 | dssdev->type = OMAP_DISPLAY_TYPE_DPI; | ||
216 | dssdev->output_type = OMAP_DISPLAY_TYPE_DVI; | ||
217 | dssdev->owner = THIS_MODULE; | ||
218 | dssdev->phy.dpi.data_lines = ddata->data_lines; | ||
219 | |||
220 | r = omapdss_register_output(dssdev); | ||
221 | if (r) { | ||
222 | dev_err(&pdev->dev, "Failed to register output\n"); | ||
223 | goto err_reg; | ||
224 | } | ||
225 | |||
226 | return 0; | ||
227 | err_reg: | ||
228 | err_gpio: | ||
229 | omap_dss_put_device(ddata->in); | ||
230 | return r; | ||
231 | } | ||
232 | |||
233 | static int __exit tfp410_remove(struct platform_device *pdev) | ||
234 | { | ||
235 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
236 | struct omap_dss_device *dssdev = &ddata->dssdev; | ||
237 | struct omap_dss_device *in = ddata->in; | ||
238 | |||
239 | omapdss_unregister_output(&ddata->dssdev); | ||
240 | |||
241 | WARN_ON(omapdss_device_is_enabled(dssdev)); | ||
242 | if (omapdss_device_is_enabled(dssdev)) | ||
243 | tfp410_disable(dssdev); | ||
244 | |||
245 | WARN_ON(omapdss_device_is_connected(dssdev)); | ||
246 | if (omapdss_device_is_connected(dssdev)) | ||
247 | tfp410_disconnect(dssdev, dssdev->device); | ||
248 | |||
249 | omap_dss_put_device(in); | ||
250 | |||
251 | return 0; | ||
252 | } | ||
253 | |||
254 | static struct platform_driver tfp410_driver = { | ||
255 | .probe = tfp410_probe, | ||
256 | .remove = __exit_p(tfp410_remove), | ||
257 | .driver = { | ||
258 | .name = "tfp410", | ||
259 | .owner = THIS_MODULE, | ||
260 | }, | ||
261 | }; | ||
262 | |||
263 | module_platform_driver(tfp410_driver); | ||
264 | |||
265 | MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); | ||
266 | MODULE_DESCRIPTION("TFP410 DPI to DVI encoder driver"); | ||
267 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/omap2/displays-new/encoder-tpd12s015.c b/drivers/video/omap2/displays-new/encoder-tpd12s015.c new file mode 100644 index 000000000000..ce0e010026cb --- /dev/null +++ b/drivers/video/omap2/displays-new/encoder-tpd12s015.c | |||
@@ -0,0 +1,395 @@ | |||
1 | /* | ||
2 | * TPD12S015 HDMI ESD protection & level shifter chip driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Texas Instruments | ||
5 | * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License version 2 as published by | ||
9 | * the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/completion.h> | ||
13 | #include <linux/delay.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/gpio.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | |||
19 | #include <video/omapdss.h> | ||
20 | #include <video/omap-panel-data.h> | ||
21 | |||
22 | struct panel_drv_data { | ||
23 | struct omap_dss_device dssdev; | ||
24 | struct omap_dss_device *in; | ||
25 | |||
26 | int ct_cp_hpd_gpio; | ||
27 | int ls_oe_gpio; | ||
28 | int hpd_gpio; | ||
29 | |||
30 | struct omap_video_timings timings; | ||
31 | |||
32 | struct completion hpd_completion; | ||
33 | }; | ||
34 | |||
35 | #define to_panel_data(x) container_of(x, struct panel_drv_data, dssdev) | ||
36 | |||
37 | static irqreturn_t tpd_hpd_irq_handler(int irq, void *data) | ||
38 | { | ||
39 | struct panel_drv_data *ddata = data; | ||
40 | bool hpd; | ||
41 | |||
42 | hpd = gpio_get_value_cansleep(ddata->hpd_gpio); | ||
43 | |||
44 | dev_dbg(ddata->dssdev.dev, "hpd %d\n", hpd); | ||
45 | |||
46 | if (gpio_is_valid(ddata->ls_oe_gpio)) { | ||
47 | if (hpd) | ||
48 | gpio_set_value_cansleep(ddata->ls_oe_gpio, 1); | ||
49 | else | ||
50 | gpio_set_value_cansleep(ddata->ls_oe_gpio, 0); | ||
51 | } | ||
52 | |||
53 | complete_all(&ddata->hpd_completion); | ||
54 | |||
55 | return IRQ_HANDLED; | ||
56 | } | ||
57 | |||
58 | static int tpd_connect(struct omap_dss_device *dssdev, | ||
59 | struct omap_dss_device *dst) | ||
60 | { | ||
61 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
62 | struct omap_dss_device *in = ddata->in; | ||
63 | int r; | ||
64 | |||
65 | r = in->ops.hdmi->connect(in, dssdev); | ||
66 | if (r) | ||
67 | return r; | ||
68 | |||
69 | dst->output = dssdev; | ||
70 | dssdev->device = dst; | ||
71 | |||
72 | INIT_COMPLETION(ddata->hpd_completion); | ||
73 | |||
74 | gpio_set_value_cansleep(ddata->ct_cp_hpd_gpio, 1); | ||
75 | /* DC-DC converter needs at max 300us to get to 90% of 5V */ | ||
76 | udelay(300); | ||
77 | |||
78 | /* | ||
79 | * If there's a cable connected, wait for the hpd irq to trigger, | ||
80 | * which turns on the level shifters. | ||
81 | */ | ||
82 | if (gpio_get_value_cansleep(ddata->hpd_gpio)) { | ||
83 | unsigned long to; | ||
84 | to = wait_for_completion_timeout(&ddata->hpd_completion, | ||
85 | msecs_to_jiffies(250)); | ||
86 | WARN_ON_ONCE(to == 0); | ||
87 | } | ||
88 | |||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | static void tpd_disconnect(struct omap_dss_device *dssdev, | ||
93 | struct omap_dss_device *dst) | ||
94 | { | ||
95 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
96 | struct omap_dss_device *in = ddata->in; | ||
97 | |||
98 | WARN_ON(dst != dssdev->device); | ||
99 | |||
100 | if (dst != dssdev->device) | ||
101 | return; | ||
102 | |||
103 | gpio_set_value_cansleep(ddata->ct_cp_hpd_gpio, 0); | ||
104 | |||
105 | dst->output = NULL; | ||
106 | dssdev->device = NULL; | ||
107 | |||
108 | in->ops.hdmi->disconnect(in, &ddata->dssdev); | ||
109 | } | ||
110 | |||
111 | static int tpd_enable(struct omap_dss_device *dssdev) | ||
112 | { | ||
113 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
114 | struct omap_dss_device *in = ddata->in; | ||
115 | int r; | ||
116 | |||
117 | if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) | ||
118 | return 0; | ||
119 | |||
120 | in->ops.hdmi->set_timings(in, &ddata->timings); | ||
121 | |||
122 | r = in->ops.hdmi->enable(in); | ||
123 | if (r) | ||
124 | return r; | ||
125 | |||
126 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | ||
127 | |||
128 | return r; | ||
129 | } | ||
130 | |||
131 | static void tpd_disable(struct omap_dss_device *dssdev) | ||
132 | { | ||
133 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
134 | struct omap_dss_device *in = ddata->in; | ||
135 | |||
136 | if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) | ||
137 | return; | ||
138 | |||
139 | in->ops.hdmi->disable(in); | ||
140 | |||
141 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; | ||
142 | } | ||
143 | |||
144 | static void tpd_set_timings(struct omap_dss_device *dssdev, | ||
145 | struct omap_video_timings *timings) | ||
146 | { | ||
147 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
148 | struct omap_dss_device *in = ddata->in; | ||
149 | |||
150 | ddata->timings = *timings; | ||
151 | dssdev->panel.timings = *timings; | ||
152 | |||
153 | in->ops.hdmi->set_timings(in, timings); | ||
154 | } | ||
155 | |||
156 | static void tpd_get_timings(struct omap_dss_device *dssdev, | ||
157 | struct omap_video_timings *timings) | ||
158 | { | ||
159 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
160 | |||
161 | *timings = ddata->timings; | ||
162 | } | ||
163 | |||
164 | static int tpd_check_timings(struct omap_dss_device *dssdev, | ||
165 | struct omap_video_timings *timings) | ||
166 | { | ||
167 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
168 | struct omap_dss_device *in = ddata->in; | ||
169 | int r; | ||
170 | |||
171 | r = in->ops.hdmi->check_timings(in, timings); | ||
172 | |||
173 | return r; | ||
174 | } | ||
175 | |||
176 | static int tpd_read_edid(struct omap_dss_device *dssdev, | ||
177 | u8 *edid, int len) | ||
178 | { | ||
179 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
180 | struct omap_dss_device *in = ddata->in; | ||
181 | |||
182 | if (!gpio_get_value_cansleep(ddata->hpd_gpio)) | ||
183 | return -ENODEV; | ||
184 | |||
185 | return in->ops.hdmi->read_edid(in, edid, len); | ||
186 | } | ||
187 | |||
188 | static bool tpd_detect(struct omap_dss_device *dssdev) | ||
189 | { | ||
190 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
191 | |||
192 | return gpio_get_value_cansleep(ddata->hpd_gpio); | ||
193 | } | ||
194 | |||
195 | static int tpd_audio_enable(struct omap_dss_device *dssdev) | ||
196 | { | ||
197 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
198 | struct omap_dss_device *in = ddata->in; | ||
199 | |||
200 | return in->ops.hdmi->audio_enable(in); | ||
201 | } | ||
202 | |||
203 | static void tpd_audio_disable(struct omap_dss_device *dssdev) | ||
204 | { | ||
205 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
206 | struct omap_dss_device *in = ddata->in; | ||
207 | |||
208 | in->ops.hdmi->audio_disable(in); | ||
209 | } | ||
210 | |||
211 | static int tpd_audio_start(struct omap_dss_device *dssdev) | ||
212 | { | ||
213 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
214 | struct omap_dss_device *in = ddata->in; | ||
215 | |||
216 | return in->ops.hdmi->audio_start(in); | ||
217 | } | ||
218 | |||
219 | static void tpd_audio_stop(struct omap_dss_device *dssdev) | ||
220 | { | ||
221 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
222 | struct omap_dss_device *in = ddata->in; | ||
223 | |||
224 | in->ops.hdmi->audio_stop(in); | ||
225 | } | ||
226 | |||
227 | static bool tpd_audio_supported(struct omap_dss_device *dssdev) | ||
228 | { | ||
229 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
230 | struct omap_dss_device *in = ddata->in; | ||
231 | |||
232 | return in->ops.hdmi->audio_supported(in); | ||
233 | } | ||
234 | |||
235 | static int tpd_audio_config(struct omap_dss_device *dssdev, | ||
236 | struct omap_dss_audio *audio) | ||
237 | { | ||
238 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
239 | struct omap_dss_device *in = ddata->in; | ||
240 | |||
241 | return in->ops.hdmi->audio_config(in, audio); | ||
242 | } | ||
243 | |||
244 | static const struct omapdss_hdmi_ops tpd_hdmi_ops = { | ||
245 | .connect = tpd_connect, | ||
246 | .disconnect = tpd_disconnect, | ||
247 | |||
248 | .enable = tpd_enable, | ||
249 | .disable = tpd_disable, | ||
250 | |||
251 | .check_timings = tpd_check_timings, | ||
252 | .set_timings = tpd_set_timings, | ||
253 | .get_timings = tpd_get_timings, | ||
254 | |||
255 | .read_edid = tpd_read_edid, | ||
256 | .detect = tpd_detect, | ||
257 | |||
258 | .audio_enable = tpd_audio_enable, | ||
259 | .audio_disable = tpd_audio_disable, | ||
260 | .audio_start = tpd_audio_start, | ||
261 | .audio_stop = tpd_audio_stop, | ||
262 | .audio_supported = tpd_audio_supported, | ||
263 | .audio_config = tpd_audio_config, | ||
264 | }; | ||
265 | |||
266 | static int tpd_probe_pdata(struct platform_device *pdev) | ||
267 | { | ||
268 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
269 | struct encoder_tpd12s015_platform_data *pdata; | ||
270 | struct omap_dss_device *dssdev, *in; | ||
271 | |||
272 | pdata = dev_get_platdata(&pdev->dev); | ||
273 | |||
274 | ddata->ct_cp_hpd_gpio = pdata->ct_cp_hpd_gpio; | ||
275 | ddata->ls_oe_gpio = pdata->ls_oe_gpio; | ||
276 | ddata->hpd_gpio = pdata->hpd_gpio; | ||
277 | |||
278 | in = omap_dss_find_output(pdata->source); | ||
279 | if (in == NULL) { | ||
280 | dev_err(&pdev->dev, "Failed to find video source\n"); | ||
281 | return -ENODEV; | ||
282 | } | ||
283 | |||
284 | ddata->in = in; | ||
285 | |||
286 | dssdev = &ddata->dssdev; | ||
287 | dssdev->name = pdata->name; | ||
288 | |||
289 | return 0; | ||
290 | } | ||
291 | |||
292 | static int tpd_probe(struct platform_device *pdev) | ||
293 | { | ||
294 | struct omap_dss_device *in, *dssdev; | ||
295 | struct panel_drv_data *ddata; | ||
296 | int r; | ||
297 | |||
298 | ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); | ||
299 | if (!ddata) | ||
300 | return -ENOMEM; | ||
301 | |||
302 | platform_set_drvdata(pdev, ddata); | ||
303 | |||
304 | init_completion(&ddata->hpd_completion); | ||
305 | |||
306 | if (dev_get_platdata(&pdev->dev)) { | ||
307 | r = tpd_probe_pdata(pdev); | ||
308 | if (r) | ||
309 | return r; | ||
310 | } else { | ||
311 | return -ENODEV; | ||
312 | } | ||
313 | |||
314 | r = devm_gpio_request_one(&pdev->dev, ddata->ct_cp_hpd_gpio, | ||
315 | GPIOF_OUT_INIT_LOW, "hdmi_ct_cp_hpd"); | ||
316 | if (r) | ||
317 | goto err_gpio; | ||
318 | |||
319 | if (gpio_is_valid(ddata->ls_oe_gpio)) { | ||
320 | r = devm_gpio_request_one(&pdev->dev, ddata->ls_oe_gpio, | ||
321 | GPIOF_OUT_INIT_LOW, "hdmi_ls_oe"); | ||
322 | if (r) | ||
323 | goto err_gpio; | ||
324 | } | ||
325 | |||
326 | r = devm_gpio_request_one(&pdev->dev, ddata->hpd_gpio, | ||
327 | GPIOF_DIR_IN, "hdmi_hpd"); | ||
328 | if (r) | ||
329 | goto err_gpio; | ||
330 | |||
331 | r = devm_request_threaded_irq(&pdev->dev, gpio_to_irq(ddata->hpd_gpio), | ||
332 | NULL, tpd_hpd_irq_handler, | ||
333 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | | ||
334 | IRQF_ONESHOT, "hpd", ddata); | ||
335 | if (r) | ||
336 | goto err_irq; | ||
337 | |||
338 | dssdev = &ddata->dssdev; | ||
339 | dssdev->ops.hdmi = &tpd_hdmi_ops; | ||
340 | dssdev->dev = &pdev->dev; | ||
341 | dssdev->type = OMAP_DISPLAY_TYPE_HDMI; | ||
342 | dssdev->output_type = OMAP_DISPLAY_TYPE_HDMI; | ||
343 | dssdev->owner = THIS_MODULE; | ||
344 | |||
345 | in = ddata->in; | ||
346 | |||
347 | r = omapdss_register_output(dssdev); | ||
348 | if (r) { | ||
349 | dev_err(&pdev->dev, "Failed to register output\n"); | ||
350 | goto err_reg; | ||
351 | } | ||
352 | |||
353 | return 0; | ||
354 | err_reg: | ||
355 | err_irq: | ||
356 | err_gpio: | ||
357 | omap_dss_put_device(ddata->in); | ||
358 | return r; | ||
359 | } | ||
360 | |||
361 | static int __exit tpd_remove(struct platform_device *pdev) | ||
362 | { | ||
363 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
364 | struct omap_dss_device *dssdev = &ddata->dssdev; | ||
365 | struct omap_dss_device *in = ddata->in; | ||
366 | |||
367 | omapdss_unregister_output(&ddata->dssdev); | ||
368 | |||
369 | WARN_ON(omapdss_device_is_enabled(dssdev)); | ||
370 | if (omapdss_device_is_enabled(dssdev)) | ||
371 | tpd_disable(dssdev); | ||
372 | |||
373 | WARN_ON(omapdss_device_is_connected(dssdev)); | ||
374 | if (omapdss_device_is_connected(dssdev)) | ||
375 | tpd_disconnect(dssdev, dssdev->device); | ||
376 | |||
377 | omap_dss_put_device(in); | ||
378 | |||
379 | return 0; | ||
380 | } | ||
381 | |||
382 | static struct platform_driver tpd_driver = { | ||
383 | .probe = tpd_probe, | ||
384 | .remove = __exit_p(tpd_remove), | ||
385 | .driver = { | ||
386 | .name = "tpd12s015", | ||
387 | .owner = THIS_MODULE, | ||
388 | }, | ||
389 | }; | ||
390 | |||
391 | module_platform_driver(tpd_driver); | ||
392 | |||
393 | MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); | ||
394 | MODULE_DESCRIPTION("TPD12S015 driver"); | ||
395 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/omap2/displays-new/panel-dpi.c b/drivers/video/omap2/displays-new/panel-dpi.c new file mode 100644 index 000000000000..5f8f7e7c81ef --- /dev/null +++ b/drivers/video/omap2/displays-new/panel-dpi.c | |||
@@ -0,0 +1,270 @@ | |||
1 | /* | ||
2 | * Generic MIPI DPI Panel Driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Texas Instruments | ||
5 | * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License version 2 as published by | ||
9 | * the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/gpio.h> | ||
13 | #include <linux/module.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/slab.h> | ||
16 | |||
17 | #include <video/omapdss.h> | ||
18 | #include <video/omap-panel-data.h> | ||
19 | |||
20 | struct panel_drv_data { | ||
21 | struct omap_dss_device dssdev; | ||
22 | struct omap_dss_device *in; | ||
23 | |||
24 | int data_lines; | ||
25 | |||
26 | struct omap_video_timings videomode; | ||
27 | |||
28 | int backlight_gpio; | ||
29 | int enable_gpio; | ||
30 | }; | ||
31 | |||
32 | #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) | ||
33 | |||
34 | static int panel_dpi_connect(struct omap_dss_device *dssdev) | ||
35 | { | ||
36 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
37 | struct omap_dss_device *in = ddata->in; | ||
38 | int r; | ||
39 | |||
40 | if (omapdss_device_is_connected(dssdev)) | ||
41 | return 0; | ||
42 | |||
43 | r = in->ops.dpi->connect(in, dssdev); | ||
44 | if (r) | ||
45 | return r; | ||
46 | |||
47 | return 0; | ||
48 | } | ||
49 | |||
50 | static void panel_dpi_disconnect(struct omap_dss_device *dssdev) | ||
51 | { | ||
52 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
53 | struct omap_dss_device *in = ddata->in; | ||
54 | |||
55 | if (!omapdss_device_is_connected(dssdev)) | ||
56 | return; | ||
57 | |||
58 | in->ops.dpi->disconnect(in, dssdev); | ||
59 | } | ||
60 | |||
61 | static int panel_dpi_enable(struct omap_dss_device *dssdev) | ||
62 | { | ||
63 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
64 | struct omap_dss_device *in = ddata->in; | ||
65 | int r; | ||
66 | |||
67 | if (!omapdss_device_is_connected(dssdev)) | ||
68 | return -ENODEV; | ||
69 | |||
70 | if (omapdss_device_is_enabled(dssdev)) | ||
71 | return 0; | ||
72 | |||
73 | in->ops.dpi->set_data_lines(in, ddata->data_lines); | ||
74 | in->ops.dpi->set_timings(in, &ddata->videomode); | ||
75 | |||
76 | r = in->ops.dpi->enable(in); | ||
77 | if (r) | ||
78 | return r; | ||
79 | |||
80 | if (gpio_is_valid(ddata->enable_gpio)) | ||
81 | gpio_set_value_cansleep(ddata->enable_gpio, 1); | ||
82 | |||
83 | if (gpio_is_valid(ddata->backlight_gpio)) | ||
84 | gpio_set_value_cansleep(ddata->backlight_gpio, 1); | ||
85 | |||
86 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | ||
87 | |||
88 | return 0; | ||
89 | } | ||
90 | |||
91 | static void panel_dpi_disable(struct omap_dss_device *dssdev) | ||
92 | { | ||
93 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
94 | struct omap_dss_device *in = ddata->in; | ||
95 | |||
96 | if (!omapdss_device_is_enabled(dssdev)) | ||
97 | return; | ||
98 | |||
99 | if (gpio_is_valid(ddata->enable_gpio)) | ||
100 | gpio_set_value_cansleep(ddata->enable_gpio, 0); | ||
101 | |||
102 | if (gpio_is_valid(ddata->backlight_gpio)) | ||
103 | gpio_set_value_cansleep(ddata->backlight_gpio, 0); | ||
104 | |||
105 | in->ops.dpi->disable(in); | ||
106 | |||
107 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; | ||
108 | } | ||
109 | |||
110 | static void panel_dpi_set_timings(struct omap_dss_device *dssdev, | ||
111 | struct omap_video_timings *timings) | ||
112 | { | ||
113 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
114 | struct omap_dss_device *in = ddata->in; | ||
115 | |||
116 | ddata->videomode = *timings; | ||
117 | dssdev->panel.timings = *timings; | ||
118 | |||
119 | in->ops.dpi->set_timings(in, timings); | ||
120 | } | ||
121 | |||
122 | static void panel_dpi_get_timings(struct omap_dss_device *dssdev, | ||
123 | struct omap_video_timings *timings) | ||
124 | { | ||
125 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
126 | |||
127 | *timings = ddata->videomode; | ||
128 | } | ||
129 | |||
130 | static int panel_dpi_check_timings(struct omap_dss_device *dssdev, | ||
131 | struct omap_video_timings *timings) | ||
132 | { | ||
133 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
134 | struct omap_dss_device *in = ddata->in; | ||
135 | |||
136 | return in->ops.dpi->check_timings(in, timings); | ||
137 | } | ||
138 | |||
139 | static struct omap_dss_driver panel_dpi_ops = { | ||
140 | .connect = panel_dpi_connect, | ||
141 | .disconnect = panel_dpi_disconnect, | ||
142 | |||
143 | .enable = panel_dpi_enable, | ||
144 | .disable = panel_dpi_disable, | ||
145 | |||
146 | .set_timings = panel_dpi_set_timings, | ||
147 | .get_timings = panel_dpi_get_timings, | ||
148 | .check_timings = panel_dpi_check_timings, | ||
149 | |||
150 | .get_resolution = omapdss_default_get_resolution, | ||
151 | }; | ||
152 | |||
153 | static int panel_dpi_probe_pdata(struct platform_device *pdev) | ||
154 | { | ||
155 | const struct panel_dpi_platform_data *pdata; | ||
156 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
157 | struct omap_dss_device *dssdev, *in; | ||
158 | struct videomode vm; | ||
159 | |||
160 | pdata = dev_get_platdata(&pdev->dev); | ||
161 | |||
162 | in = omap_dss_find_output(pdata->source); | ||
163 | if (in == NULL) { | ||
164 | dev_err(&pdev->dev, "failed to find video source '%s'\n", | ||
165 | pdata->source); | ||
166 | return -EPROBE_DEFER; | ||
167 | } | ||
168 | |||
169 | ddata->in = in; | ||
170 | |||
171 | ddata->data_lines = pdata->data_lines; | ||
172 | |||
173 | videomode_from_timing(pdata->display_timing, &vm); | ||
174 | videomode_to_omap_video_timings(&vm, &ddata->videomode); | ||
175 | |||
176 | dssdev = &ddata->dssdev; | ||
177 | dssdev->name = pdata->name; | ||
178 | |||
179 | ddata->enable_gpio = pdata->enable_gpio; | ||
180 | ddata->backlight_gpio = pdata->backlight_gpio; | ||
181 | |||
182 | return 0; | ||
183 | } | ||
184 | |||
185 | static int panel_dpi_probe(struct platform_device *pdev) | ||
186 | { | ||
187 | struct panel_drv_data *ddata; | ||
188 | struct omap_dss_device *dssdev; | ||
189 | int r; | ||
190 | |||
191 | ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); | ||
192 | if (ddata == NULL) | ||
193 | return -ENOMEM; | ||
194 | |||
195 | platform_set_drvdata(pdev, ddata); | ||
196 | |||
197 | if (dev_get_platdata(&pdev->dev)) { | ||
198 | r = panel_dpi_probe_pdata(pdev); | ||
199 | if (r) | ||
200 | return r; | ||
201 | } else { | ||
202 | return -ENODEV; | ||
203 | } | ||
204 | |||
205 | if (gpio_is_valid(ddata->enable_gpio)) { | ||
206 | r = devm_gpio_request_one(&pdev->dev, ddata->enable_gpio, | ||
207 | GPIOF_OUT_INIT_LOW, "panel enable"); | ||
208 | if (r) | ||
209 | goto err_gpio; | ||
210 | } | ||
211 | |||
212 | if (gpio_is_valid(ddata->backlight_gpio)) { | ||
213 | r = devm_gpio_request_one(&pdev->dev, ddata->backlight_gpio, | ||
214 | GPIOF_OUT_INIT_LOW, "panel backlight"); | ||
215 | if (r) | ||
216 | goto err_gpio; | ||
217 | } | ||
218 | |||
219 | dssdev = &ddata->dssdev; | ||
220 | dssdev->dev = &pdev->dev; | ||
221 | dssdev->driver = &panel_dpi_ops; | ||
222 | dssdev->type = OMAP_DISPLAY_TYPE_DPI; | ||
223 | dssdev->owner = THIS_MODULE; | ||
224 | dssdev->panel.timings = ddata->videomode; | ||
225 | dssdev->phy.dpi.data_lines = ddata->data_lines; | ||
226 | |||
227 | r = omapdss_register_display(dssdev); | ||
228 | if (r) { | ||
229 | dev_err(&pdev->dev, "Failed to register panel\n"); | ||
230 | goto err_reg; | ||
231 | } | ||
232 | |||
233 | return 0; | ||
234 | |||
235 | err_reg: | ||
236 | err_gpio: | ||
237 | omap_dss_put_device(ddata->in); | ||
238 | return r; | ||
239 | } | ||
240 | |||
241 | static int __exit panel_dpi_remove(struct platform_device *pdev) | ||
242 | { | ||
243 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
244 | struct omap_dss_device *dssdev = &ddata->dssdev; | ||
245 | struct omap_dss_device *in = ddata->in; | ||
246 | |||
247 | omapdss_unregister_display(dssdev); | ||
248 | |||
249 | panel_dpi_disable(dssdev); | ||
250 | panel_dpi_disconnect(dssdev); | ||
251 | |||
252 | omap_dss_put_device(in); | ||
253 | |||
254 | return 0; | ||
255 | } | ||
256 | |||
257 | static struct platform_driver panel_dpi_driver = { | ||
258 | .probe = panel_dpi_probe, | ||
259 | .remove = __exit_p(panel_dpi_remove), | ||
260 | .driver = { | ||
261 | .name = "panel-dpi", | ||
262 | .owner = THIS_MODULE, | ||
263 | }, | ||
264 | }; | ||
265 | |||
266 | module_platform_driver(panel_dpi_driver); | ||
267 | |||
268 | MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); | ||
269 | MODULE_DESCRIPTION("Generic MIPI DPI Panel Driver"); | ||
270 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/omap2/displays-new/panel-dsi-cm.c b/drivers/video/omap2/displays-new/panel-dsi-cm.c new file mode 100644 index 000000000000..aaaea6469cd9 --- /dev/null +++ b/drivers/video/omap2/displays-new/panel-dsi-cm.c | |||
@@ -0,0 +1,1336 @@ | |||
1 | /* | ||
2 | * Generic DSI Command Mode panel driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Texas Instruments | ||
5 | * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License version 2 as published by | ||
9 | * the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | /* #define DEBUG */ | ||
13 | |||
14 | #include <linux/backlight.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/fb.h> | ||
17 | #include <linux/gpio.h> | ||
18 | #include <linux/interrupt.h> | ||
19 | #include <linux/jiffies.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/sched.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/workqueue.h> | ||
25 | |||
26 | #include <video/omapdss.h> | ||
27 | #include <video/omap-panel-data.h> | ||
28 | #include <video/mipi_display.h> | ||
29 | |||
30 | /* DSI Virtual channel. Hardcoded for now. */ | ||
31 | #define TCH 0 | ||
32 | |||
33 | #define DCS_READ_NUM_ERRORS 0x05 | ||
34 | #define DCS_BRIGHTNESS 0x51 | ||
35 | #define DCS_CTRL_DISPLAY 0x53 | ||
36 | #define DCS_GET_ID1 0xda | ||
37 | #define DCS_GET_ID2 0xdb | ||
38 | #define DCS_GET_ID3 0xdc | ||
39 | |||
40 | struct panel_drv_data { | ||
41 | struct omap_dss_device dssdev; | ||
42 | struct omap_dss_device *in; | ||
43 | |||
44 | struct omap_video_timings timings; | ||
45 | |||
46 | struct platform_device *pdev; | ||
47 | |||
48 | struct mutex lock; | ||
49 | |||
50 | struct backlight_device *bldev; | ||
51 | |||
52 | unsigned long hw_guard_end; /* next value of jiffies when we can | ||
53 | * issue the next sleep in/out command | ||
54 | */ | ||
55 | unsigned long hw_guard_wait; /* max guard time in jiffies */ | ||
56 | |||
57 | /* panel HW configuration from DT or platform data */ | ||
58 | int reset_gpio; | ||
59 | int ext_te_gpio; | ||
60 | |||
61 | bool use_dsi_backlight; | ||
62 | |||
63 | struct omap_dsi_pin_config pin_config; | ||
64 | |||
65 | /* runtime variables */ | ||
66 | bool enabled; | ||
67 | |||
68 | bool te_enabled; | ||
69 | |||
70 | atomic_t do_update; | ||
71 | int channel; | ||
72 | |||
73 | struct delayed_work te_timeout_work; | ||
74 | |||
75 | bool intro_printed; | ||
76 | |||
77 | struct workqueue_struct *workqueue; | ||
78 | |||
79 | bool ulps_enabled; | ||
80 | unsigned ulps_timeout; | ||
81 | struct delayed_work ulps_work; | ||
82 | }; | ||
83 | |||
84 | #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) | ||
85 | |||
86 | static irqreturn_t dsicm_te_isr(int irq, void *data); | ||
87 | static void dsicm_te_timeout_work_callback(struct work_struct *work); | ||
88 | static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable); | ||
89 | |||
90 | static int dsicm_panel_reset(struct panel_drv_data *ddata); | ||
91 | |||
92 | static void dsicm_ulps_work(struct work_struct *work); | ||
93 | |||
94 | static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec) | ||
95 | { | ||
96 | ddata->hw_guard_wait = msecs_to_jiffies(guard_msec); | ||
97 | ddata->hw_guard_end = jiffies + ddata->hw_guard_wait; | ||
98 | } | ||
99 | |||
100 | static void hw_guard_wait(struct panel_drv_data *ddata) | ||
101 | { | ||
102 | unsigned long wait = ddata->hw_guard_end - jiffies; | ||
103 | |||
104 | if ((long)wait > 0 && wait <= ddata->hw_guard_wait) { | ||
105 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
106 | schedule_timeout(wait); | ||
107 | } | ||
108 | } | ||
109 | |||
110 | static int dsicm_dcs_read_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 *data) | ||
111 | { | ||
112 | struct omap_dss_device *in = ddata->in; | ||
113 | int r; | ||
114 | u8 buf[1]; | ||
115 | |||
116 | r = in->ops.dsi->dcs_read(in, ddata->channel, dcs_cmd, buf, 1); | ||
117 | |||
118 | if (r < 0) | ||
119 | return r; | ||
120 | |||
121 | *data = buf[0]; | ||
122 | |||
123 | return 0; | ||
124 | } | ||
125 | |||
126 | static int dsicm_dcs_write_0(struct panel_drv_data *ddata, u8 dcs_cmd) | ||
127 | { | ||
128 | struct omap_dss_device *in = ddata->in; | ||
129 | return in->ops.dsi->dcs_write(in, ddata->channel, &dcs_cmd, 1); | ||
130 | } | ||
131 | |||
132 | static int dsicm_dcs_write_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 param) | ||
133 | { | ||
134 | struct omap_dss_device *in = ddata->in; | ||
135 | u8 buf[2] = { dcs_cmd, param }; | ||
136 | |||
137 | return in->ops.dsi->dcs_write(in, ddata->channel, buf, 2); | ||
138 | } | ||
139 | |||
140 | static int dsicm_sleep_in(struct panel_drv_data *ddata) | ||
141 | |||
142 | { | ||
143 | struct omap_dss_device *in = ddata->in; | ||
144 | u8 cmd; | ||
145 | int r; | ||
146 | |||
147 | hw_guard_wait(ddata); | ||
148 | |||
149 | cmd = MIPI_DCS_ENTER_SLEEP_MODE; | ||
150 | r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, &cmd, 1); | ||
151 | if (r) | ||
152 | return r; | ||
153 | |||
154 | hw_guard_start(ddata, 120); | ||
155 | |||
156 | usleep_range(5000, 10000); | ||
157 | |||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | static int dsicm_sleep_out(struct panel_drv_data *ddata) | ||
162 | { | ||
163 | int r; | ||
164 | |||
165 | hw_guard_wait(ddata); | ||
166 | |||
167 | r = dsicm_dcs_write_0(ddata, MIPI_DCS_EXIT_SLEEP_MODE); | ||
168 | if (r) | ||
169 | return r; | ||
170 | |||
171 | hw_guard_start(ddata, 120); | ||
172 | |||
173 | usleep_range(5000, 10000); | ||
174 | |||
175 | return 0; | ||
176 | } | ||
177 | |||
178 | static int dsicm_get_id(struct panel_drv_data *ddata, u8 *id1, u8 *id2, u8 *id3) | ||
179 | { | ||
180 | int r; | ||
181 | |||
182 | r = dsicm_dcs_read_1(ddata, DCS_GET_ID1, id1); | ||
183 | if (r) | ||
184 | return r; | ||
185 | r = dsicm_dcs_read_1(ddata, DCS_GET_ID2, id2); | ||
186 | if (r) | ||
187 | return r; | ||
188 | r = dsicm_dcs_read_1(ddata, DCS_GET_ID3, id3); | ||
189 | if (r) | ||
190 | return r; | ||
191 | |||
192 | return 0; | ||
193 | } | ||
194 | |||
195 | static int dsicm_set_update_window(struct panel_drv_data *ddata, | ||
196 | u16 x, u16 y, u16 w, u16 h) | ||
197 | { | ||
198 | struct omap_dss_device *in = ddata->in; | ||
199 | int r; | ||
200 | u16 x1 = x; | ||
201 | u16 x2 = x + w - 1; | ||
202 | u16 y1 = y; | ||
203 | u16 y2 = y + h - 1; | ||
204 | |||
205 | u8 buf[5]; | ||
206 | buf[0] = MIPI_DCS_SET_COLUMN_ADDRESS; | ||
207 | buf[1] = (x1 >> 8) & 0xff; | ||
208 | buf[2] = (x1 >> 0) & 0xff; | ||
209 | buf[3] = (x2 >> 8) & 0xff; | ||
210 | buf[4] = (x2 >> 0) & 0xff; | ||
211 | |||
212 | r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, buf, sizeof(buf)); | ||
213 | if (r) | ||
214 | return r; | ||
215 | |||
216 | buf[0] = MIPI_DCS_SET_PAGE_ADDRESS; | ||
217 | buf[1] = (y1 >> 8) & 0xff; | ||
218 | buf[2] = (y1 >> 0) & 0xff; | ||
219 | buf[3] = (y2 >> 8) & 0xff; | ||
220 | buf[4] = (y2 >> 0) & 0xff; | ||
221 | |||
222 | r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, buf, sizeof(buf)); | ||
223 | if (r) | ||
224 | return r; | ||
225 | |||
226 | in->ops.dsi->bta_sync(in, ddata->channel); | ||
227 | |||
228 | return r; | ||
229 | } | ||
230 | |||
231 | static void dsicm_queue_ulps_work(struct panel_drv_data *ddata) | ||
232 | { | ||
233 | if (ddata->ulps_timeout > 0) | ||
234 | queue_delayed_work(ddata->workqueue, &ddata->ulps_work, | ||
235 | msecs_to_jiffies(ddata->ulps_timeout)); | ||
236 | } | ||
237 | |||
238 | static void dsicm_cancel_ulps_work(struct panel_drv_data *ddata) | ||
239 | { | ||
240 | cancel_delayed_work(&ddata->ulps_work); | ||
241 | } | ||
242 | |||
243 | static int dsicm_enter_ulps(struct panel_drv_data *ddata) | ||
244 | { | ||
245 | struct omap_dss_device *in = ddata->in; | ||
246 | int r; | ||
247 | |||
248 | if (ddata->ulps_enabled) | ||
249 | return 0; | ||
250 | |||
251 | dsicm_cancel_ulps_work(ddata); | ||
252 | |||
253 | r = _dsicm_enable_te(ddata, false); | ||
254 | if (r) | ||
255 | goto err; | ||
256 | |||
257 | if (gpio_is_valid(ddata->ext_te_gpio)) | ||
258 | disable_irq(gpio_to_irq(ddata->ext_te_gpio)); | ||
259 | |||
260 | in->ops.dsi->disable(in, false, true); | ||
261 | |||
262 | ddata->ulps_enabled = true; | ||
263 | |||
264 | return 0; | ||
265 | |||
266 | err: | ||
267 | dev_err(&ddata->pdev->dev, "enter ULPS failed"); | ||
268 | dsicm_panel_reset(ddata); | ||
269 | |||
270 | ddata->ulps_enabled = false; | ||
271 | |||
272 | dsicm_queue_ulps_work(ddata); | ||
273 | |||
274 | return r; | ||
275 | } | ||
276 | |||
277 | static int dsicm_exit_ulps(struct panel_drv_data *ddata) | ||
278 | { | ||
279 | struct omap_dss_device *in = ddata->in; | ||
280 | int r; | ||
281 | |||
282 | if (!ddata->ulps_enabled) | ||
283 | return 0; | ||
284 | |||
285 | r = in->ops.dsi->enable(in); | ||
286 | if (r) { | ||
287 | dev_err(&ddata->pdev->dev, "failed to enable DSI\n"); | ||
288 | goto err1; | ||
289 | } | ||
290 | |||
291 | in->ops.dsi->enable_hs(in, ddata->channel, true); | ||
292 | |||
293 | r = _dsicm_enable_te(ddata, true); | ||
294 | if (r) { | ||
295 | dev_err(&ddata->pdev->dev, "failed to re-enable TE"); | ||
296 | goto err2; | ||
297 | } | ||
298 | |||
299 | if (gpio_is_valid(ddata->ext_te_gpio)) | ||
300 | enable_irq(gpio_to_irq(ddata->ext_te_gpio)); | ||
301 | |||
302 | dsicm_queue_ulps_work(ddata); | ||
303 | |||
304 | ddata->ulps_enabled = false; | ||
305 | |||
306 | return 0; | ||
307 | |||
308 | err2: | ||
309 | dev_err(&ddata->pdev->dev, "failed to exit ULPS"); | ||
310 | |||
311 | r = dsicm_panel_reset(ddata); | ||
312 | if (!r) { | ||
313 | if (gpio_is_valid(ddata->ext_te_gpio)) | ||
314 | enable_irq(gpio_to_irq(ddata->ext_te_gpio)); | ||
315 | ddata->ulps_enabled = false; | ||
316 | } | ||
317 | err1: | ||
318 | dsicm_queue_ulps_work(ddata); | ||
319 | |||
320 | return r; | ||
321 | } | ||
322 | |||
323 | static int dsicm_wake_up(struct panel_drv_data *ddata) | ||
324 | { | ||
325 | if (ddata->ulps_enabled) | ||
326 | return dsicm_exit_ulps(ddata); | ||
327 | |||
328 | dsicm_cancel_ulps_work(ddata); | ||
329 | dsicm_queue_ulps_work(ddata); | ||
330 | return 0; | ||
331 | } | ||
332 | |||
333 | static int dsicm_bl_update_status(struct backlight_device *dev) | ||
334 | { | ||
335 | struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev); | ||
336 | struct omap_dss_device *in = ddata->in; | ||
337 | int r; | ||
338 | int level; | ||
339 | |||
340 | if (dev->props.fb_blank == FB_BLANK_UNBLANK && | ||
341 | dev->props.power == FB_BLANK_UNBLANK) | ||
342 | level = dev->props.brightness; | ||
343 | else | ||
344 | level = 0; | ||
345 | |||
346 | dev_dbg(&ddata->pdev->dev, "update brightness to %d\n", level); | ||
347 | |||
348 | mutex_lock(&ddata->lock); | ||
349 | |||
350 | if (ddata->enabled) { | ||
351 | in->ops.dsi->bus_lock(in); | ||
352 | |||
353 | r = dsicm_wake_up(ddata); | ||
354 | if (!r) | ||
355 | r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, level); | ||
356 | |||
357 | in->ops.dsi->bus_unlock(in); | ||
358 | } else { | ||
359 | r = 0; | ||
360 | } | ||
361 | |||
362 | mutex_unlock(&ddata->lock); | ||
363 | |||
364 | return r; | ||
365 | } | ||
366 | |||
367 | static int dsicm_bl_get_intensity(struct backlight_device *dev) | ||
368 | { | ||
369 | if (dev->props.fb_blank == FB_BLANK_UNBLANK && | ||
370 | dev->props.power == FB_BLANK_UNBLANK) | ||
371 | return dev->props.brightness; | ||
372 | |||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | static const struct backlight_ops dsicm_bl_ops = { | ||
377 | .get_brightness = dsicm_bl_get_intensity, | ||
378 | .update_status = dsicm_bl_update_status, | ||
379 | }; | ||
380 | |||
381 | static void dsicm_get_resolution(struct omap_dss_device *dssdev, | ||
382 | u16 *xres, u16 *yres) | ||
383 | { | ||
384 | *xres = dssdev->panel.timings.x_res; | ||
385 | *yres = dssdev->panel.timings.y_res; | ||
386 | } | ||
387 | |||
388 | static ssize_t dsicm_num_errors_show(struct device *dev, | ||
389 | struct device_attribute *attr, char *buf) | ||
390 | { | ||
391 | struct platform_device *pdev = to_platform_device(dev); | ||
392 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
393 | struct omap_dss_device *in = ddata->in; | ||
394 | u8 errors = 0; | ||
395 | int r; | ||
396 | |||
397 | mutex_lock(&ddata->lock); | ||
398 | |||
399 | if (ddata->enabled) { | ||
400 | in->ops.dsi->bus_lock(in); | ||
401 | |||
402 | r = dsicm_wake_up(ddata); | ||
403 | if (!r) | ||
404 | r = dsicm_dcs_read_1(ddata, DCS_READ_NUM_ERRORS, | ||
405 | &errors); | ||
406 | |||
407 | in->ops.dsi->bus_unlock(in); | ||
408 | } else { | ||
409 | r = -ENODEV; | ||
410 | } | ||
411 | |||
412 | mutex_unlock(&ddata->lock); | ||
413 | |||
414 | if (r) | ||
415 | return r; | ||
416 | |||
417 | return snprintf(buf, PAGE_SIZE, "%d\n", errors); | ||
418 | } | ||
419 | |||
420 | static ssize_t dsicm_hw_revision_show(struct device *dev, | ||
421 | struct device_attribute *attr, char *buf) | ||
422 | { | ||
423 | struct platform_device *pdev = to_platform_device(dev); | ||
424 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
425 | struct omap_dss_device *in = ddata->in; | ||
426 | u8 id1, id2, id3; | ||
427 | int r; | ||
428 | |||
429 | mutex_lock(&ddata->lock); | ||
430 | |||
431 | if (ddata->enabled) { | ||
432 | in->ops.dsi->bus_lock(in); | ||
433 | |||
434 | r = dsicm_wake_up(ddata); | ||
435 | if (!r) | ||
436 | r = dsicm_get_id(ddata, &id1, &id2, &id3); | ||
437 | |||
438 | in->ops.dsi->bus_unlock(in); | ||
439 | } else { | ||
440 | r = -ENODEV; | ||
441 | } | ||
442 | |||
443 | mutex_unlock(&ddata->lock); | ||
444 | |||
445 | if (r) | ||
446 | return r; | ||
447 | |||
448 | return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3); | ||
449 | } | ||
450 | |||
451 | static ssize_t dsicm_store_ulps(struct device *dev, | ||
452 | struct device_attribute *attr, | ||
453 | const char *buf, size_t count) | ||
454 | { | ||
455 | struct platform_device *pdev = to_platform_device(dev); | ||
456 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
457 | struct omap_dss_device *in = ddata->in; | ||
458 | unsigned long t; | ||
459 | int r; | ||
460 | |||
461 | r = kstrtoul(buf, 0, &t); | ||
462 | if (r) | ||
463 | return r; | ||
464 | |||
465 | mutex_lock(&ddata->lock); | ||
466 | |||
467 | if (ddata->enabled) { | ||
468 | in->ops.dsi->bus_lock(in); | ||
469 | |||
470 | if (t) | ||
471 | r = dsicm_enter_ulps(ddata); | ||
472 | else | ||
473 | r = dsicm_wake_up(ddata); | ||
474 | |||
475 | in->ops.dsi->bus_unlock(in); | ||
476 | } | ||
477 | |||
478 | mutex_unlock(&ddata->lock); | ||
479 | |||
480 | if (r) | ||
481 | return r; | ||
482 | |||
483 | return count; | ||
484 | } | ||
485 | |||
486 | static ssize_t dsicm_show_ulps(struct device *dev, | ||
487 | struct device_attribute *attr, | ||
488 | char *buf) | ||
489 | { | ||
490 | struct platform_device *pdev = to_platform_device(dev); | ||
491 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
492 | unsigned t; | ||
493 | |||
494 | mutex_lock(&ddata->lock); | ||
495 | t = ddata->ulps_enabled; | ||
496 | mutex_unlock(&ddata->lock); | ||
497 | |||
498 | return snprintf(buf, PAGE_SIZE, "%u\n", t); | ||
499 | } | ||
500 | |||
501 | static ssize_t dsicm_store_ulps_timeout(struct device *dev, | ||
502 | struct device_attribute *attr, | ||
503 | const char *buf, size_t count) | ||
504 | { | ||
505 | struct platform_device *pdev = to_platform_device(dev); | ||
506 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
507 | struct omap_dss_device *in = ddata->in; | ||
508 | unsigned long t; | ||
509 | int r; | ||
510 | |||
511 | r = kstrtoul(buf, 0, &t); | ||
512 | if (r) | ||
513 | return r; | ||
514 | |||
515 | mutex_lock(&ddata->lock); | ||
516 | ddata->ulps_timeout = t; | ||
517 | |||
518 | if (ddata->enabled) { | ||
519 | /* dsicm_wake_up will restart the timer */ | ||
520 | in->ops.dsi->bus_lock(in); | ||
521 | r = dsicm_wake_up(ddata); | ||
522 | in->ops.dsi->bus_unlock(in); | ||
523 | } | ||
524 | |||
525 | mutex_unlock(&ddata->lock); | ||
526 | |||
527 | if (r) | ||
528 | return r; | ||
529 | |||
530 | return count; | ||
531 | } | ||
532 | |||
533 | static ssize_t dsicm_show_ulps_timeout(struct device *dev, | ||
534 | struct device_attribute *attr, | ||
535 | char *buf) | ||
536 | { | ||
537 | struct platform_device *pdev = to_platform_device(dev); | ||
538 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
539 | unsigned t; | ||
540 | |||
541 | mutex_lock(&ddata->lock); | ||
542 | t = ddata->ulps_timeout; | ||
543 | mutex_unlock(&ddata->lock); | ||
544 | |||
545 | return snprintf(buf, PAGE_SIZE, "%u\n", t); | ||
546 | } | ||
547 | |||
548 | static DEVICE_ATTR(num_dsi_errors, S_IRUGO, dsicm_num_errors_show, NULL); | ||
549 | static DEVICE_ATTR(hw_revision, S_IRUGO, dsicm_hw_revision_show, NULL); | ||
550 | static DEVICE_ATTR(ulps, S_IRUGO | S_IWUSR, | ||
551 | dsicm_show_ulps, dsicm_store_ulps); | ||
552 | static DEVICE_ATTR(ulps_timeout, S_IRUGO | S_IWUSR, | ||
553 | dsicm_show_ulps_timeout, dsicm_store_ulps_timeout); | ||
554 | |||
555 | static struct attribute *dsicm_attrs[] = { | ||
556 | &dev_attr_num_dsi_errors.attr, | ||
557 | &dev_attr_hw_revision.attr, | ||
558 | &dev_attr_ulps.attr, | ||
559 | &dev_attr_ulps_timeout.attr, | ||
560 | NULL, | ||
561 | }; | ||
562 | |||
563 | static struct attribute_group dsicm_attr_group = { | ||
564 | .attrs = dsicm_attrs, | ||
565 | }; | ||
566 | |||
567 | static void dsicm_hw_reset(struct panel_drv_data *ddata) | ||
568 | { | ||
569 | if (!gpio_is_valid(ddata->reset_gpio)) | ||
570 | return; | ||
571 | |||
572 | gpio_set_value(ddata->reset_gpio, 1); | ||
573 | udelay(10); | ||
574 | /* reset the panel */ | ||
575 | gpio_set_value(ddata->reset_gpio, 0); | ||
576 | /* assert reset */ | ||
577 | udelay(10); | ||
578 | gpio_set_value(ddata->reset_gpio, 1); | ||
579 | /* wait after releasing reset */ | ||
580 | usleep_range(5000, 10000); | ||
581 | } | ||
582 | |||
583 | static int dsicm_power_on(struct panel_drv_data *ddata) | ||
584 | { | ||
585 | struct omap_dss_device *in = ddata->in; | ||
586 | u8 id1, id2, id3; | ||
587 | int r; | ||
588 | struct omap_dss_dsi_config dsi_config = { | ||
589 | .mode = OMAP_DSS_DSI_CMD_MODE, | ||
590 | .pixel_format = OMAP_DSS_DSI_FMT_RGB888, | ||
591 | .timings = &ddata->timings, | ||
592 | .hs_clk_min = 150000000, | ||
593 | .hs_clk_max = 300000000, | ||
594 | .lp_clk_min = 7000000, | ||
595 | .lp_clk_max = 10000000, | ||
596 | }; | ||
597 | |||
598 | r = in->ops.dsi->configure_pins(in, &ddata->pin_config); | ||
599 | if (r) { | ||
600 | dev_err(&ddata->pdev->dev, "failed to configure DSI pins\n"); | ||
601 | goto err0; | ||
602 | }; | ||
603 | |||
604 | r = in->ops.dsi->set_config(in, &dsi_config); | ||
605 | if (r) { | ||
606 | dev_err(&ddata->pdev->dev, "failed to configure DSI\n"); | ||
607 | goto err0; | ||
608 | } | ||
609 | |||
610 | r = in->ops.dsi->enable(in); | ||
611 | if (r) { | ||
612 | dev_err(&ddata->pdev->dev, "failed to enable DSI\n"); | ||
613 | goto err0; | ||
614 | } | ||
615 | |||
616 | dsicm_hw_reset(ddata); | ||
617 | |||
618 | in->ops.dsi->enable_hs(in, ddata->channel, false); | ||
619 | |||
620 | r = dsicm_sleep_out(ddata); | ||
621 | if (r) | ||
622 | goto err; | ||
623 | |||
624 | r = dsicm_get_id(ddata, &id1, &id2, &id3); | ||
625 | if (r) | ||
626 | goto err; | ||
627 | |||
628 | r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, 0xff); | ||
629 | if (r) | ||
630 | goto err; | ||
631 | |||
632 | r = dsicm_dcs_write_1(ddata, DCS_CTRL_DISPLAY, | ||
633 | (1<<2) | (1<<5)); /* BL | BCTRL */ | ||
634 | if (r) | ||
635 | goto err; | ||
636 | |||
637 | r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_PIXEL_FORMAT, | ||
638 | MIPI_DCS_PIXEL_FMT_24BIT); | ||
639 | if (r) | ||
640 | goto err; | ||
641 | |||
642 | r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_DISPLAY_ON); | ||
643 | if (r) | ||
644 | goto err; | ||
645 | |||
646 | r = _dsicm_enable_te(ddata, ddata->te_enabled); | ||
647 | if (r) | ||
648 | goto err; | ||
649 | |||
650 | r = in->ops.dsi->enable_video_output(in, ddata->channel); | ||
651 | if (r) | ||
652 | goto err; | ||
653 | |||
654 | ddata->enabled = 1; | ||
655 | |||
656 | if (!ddata->intro_printed) { | ||
657 | dev_info(&ddata->pdev->dev, "panel revision %02x.%02x.%02x\n", | ||
658 | id1, id2, id3); | ||
659 | ddata->intro_printed = true; | ||
660 | } | ||
661 | |||
662 | in->ops.dsi->enable_hs(in, ddata->channel, true); | ||
663 | |||
664 | return 0; | ||
665 | err: | ||
666 | dev_err(&ddata->pdev->dev, "error while enabling panel, issuing HW reset\n"); | ||
667 | |||
668 | dsicm_hw_reset(ddata); | ||
669 | |||
670 | in->ops.dsi->disable(in, true, false); | ||
671 | err0: | ||
672 | return r; | ||
673 | } | ||
674 | |||
675 | static void dsicm_power_off(struct panel_drv_data *ddata) | ||
676 | { | ||
677 | struct omap_dss_device *in = ddata->in; | ||
678 | int r; | ||
679 | |||
680 | in->ops.dsi->disable_video_output(in, ddata->channel); | ||
681 | |||
682 | r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_DISPLAY_OFF); | ||
683 | if (!r) | ||
684 | r = dsicm_sleep_in(ddata); | ||
685 | |||
686 | if (r) { | ||
687 | dev_err(&ddata->pdev->dev, | ||
688 | "error disabling panel, issuing HW reset\n"); | ||
689 | dsicm_hw_reset(ddata); | ||
690 | } | ||
691 | |||
692 | in->ops.dsi->disable(in, true, false); | ||
693 | |||
694 | ddata->enabled = 0; | ||
695 | } | ||
696 | |||
697 | static int dsicm_panel_reset(struct panel_drv_data *ddata) | ||
698 | { | ||
699 | dev_err(&ddata->pdev->dev, "performing LCD reset\n"); | ||
700 | |||
701 | dsicm_power_off(ddata); | ||
702 | dsicm_hw_reset(ddata); | ||
703 | return dsicm_power_on(ddata); | ||
704 | } | ||
705 | |||
706 | static int dsicm_connect(struct omap_dss_device *dssdev) | ||
707 | { | ||
708 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
709 | struct omap_dss_device *in = ddata->in; | ||
710 | struct device *dev = &ddata->pdev->dev; | ||
711 | int r; | ||
712 | |||
713 | if (omapdss_device_is_connected(dssdev)) | ||
714 | return 0; | ||
715 | |||
716 | r = in->ops.dsi->connect(in, dssdev); | ||
717 | if (r) { | ||
718 | dev_err(dev, "Failed to connect to video source\n"); | ||
719 | return r; | ||
720 | } | ||
721 | |||
722 | r = in->ops.dsi->request_vc(ddata->in, &ddata->channel); | ||
723 | if (r) { | ||
724 | dev_err(dev, "failed to get virtual channel\n"); | ||
725 | goto err_req_vc; | ||
726 | } | ||
727 | |||
728 | r = in->ops.dsi->set_vc_id(ddata->in, ddata->channel, TCH); | ||
729 | if (r) { | ||
730 | dev_err(dev, "failed to set VC_ID\n"); | ||
731 | goto err_vc_id; | ||
732 | } | ||
733 | |||
734 | return 0; | ||
735 | |||
736 | err_vc_id: | ||
737 | in->ops.dsi->release_vc(ddata->in, ddata->channel); | ||
738 | err_req_vc: | ||
739 | in->ops.dsi->disconnect(in, dssdev); | ||
740 | return r; | ||
741 | } | ||
742 | |||
743 | static void dsicm_disconnect(struct omap_dss_device *dssdev) | ||
744 | { | ||
745 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
746 | struct omap_dss_device *in = ddata->in; | ||
747 | |||
748 | if (!omapdss_device_is_connected(dssdev)) | ||
749 | return; | ||
750 | |||
751 | in->ops.dsi->release_vc(in, ddata->channel); | ||
752 | in->ops.dsi->disconnect(in, dssdev); | ||
753 | } | ||
754 | |||
755 | static int dsicm_enable(struct omap_dss_device *dssdev) | ||
756 | { | ||
757 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
758 | struct omap_dss_device *in = ddata->in; | ||
759 | int r; | ||
760 | |||
761 | dev_dbg(&ddata->pdev->dev, "enable\n"); | ||
762 | |||
763 | mutex_lock(&ddata->lock); | ||
764 | |||
765 | if (!omapdss_device_is_connected(dssdev)) { | ||
766 | r = -ENODEV; | ||
767 | goto err; | ||
768 | } | ||
769 | |||
770 | if (omapdss_device_is_enabled(dssdev)) { | ||
771 | r = 0; | ||
772 | goto err; | ||
773 | } | ||
774 | |||
775 | in->ops.dsi->bus_lock(in); | ||
776 | |||
777 | r = dsicm_power_on(ddata); | ||
778 | |||
779 | in->ops.dsi->bus_unlock(in); | ||
780 | |||
781 | if (r) | ||
782 | goto err; | ||
783 | |||
784 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | ||
785 | |||
786 | mutex_unlock(&ddata->lock); | ||
787 | |||
788 | return 0; | ||
789 | err: | ||
790 | dev_dbg(&ddata->pdev->dev, "enable failed\n"); | ||
791 | mutex_unlock(&ddata->lock); | ||
792 | return r; | ||
793 | } | ||
794 | |||
795 | static void dsicm_disable(struct omap_dss_device *dssdev) | ||
796 | { | ||
797 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
798 | struct omap_dss_device *in = ddata->in; | ||
799 | int r; | ||
800 | |||
801 | dev_dbg(&ddata->pdev->dev, "disable\n"); | ||
802 | |||
803 | mutex_lock(&ddata->lock); | ||
804 | |||
805 | dsicm_cancel_ulps_work(ddata); | ||
806 | |||
807 | in->ops.dsi->bus_lock(in); | ||
808 | |||
809 | if (omapdss_device_is_enabled(dssdev)) { | ||
810 | r = dsicm_wake_up(ddata); | ||
811 | if (!r) | ||
812 | dsicm_power_off(ddata); | ||
813 | } | ||
814 | |||
815 | in->ops.dsi->bus_unlock(in); | ||
816 | |||
817 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; | ||
818 | |||
819 | mutex_unlock(&ddata->lock); | ||
820 | } | ||
821 | |||
822 | static void dsicm_framedone_cb(int err, void *data) | ||
823 | { | ||
824 | struct panel_drv_data *ddata = data; | ||
825 | struct omap_dss_device *in = ddata->in; | ||
826 | |||
827 | dev_dbg(&ddata->pdev->dev, "framedone, err %d\n", err); | ||
828 | in->ops.dsi->bus_unlock(ddata->in); | ||
829 | } | ||
830 | |||
831 | static irqreturn_t dsicm_te_isr(int irq, void *data) | ||
832 | { | ||
833 | struct panel_drv_data *ddata = data; | ||
834 | struct omap_dss_device *in = ddata->in; | ||
835 | int old; | ||
836 | int r; | ||
837 | |||
838 | old = atomic_cmpxchg(&ddata->do_update, 1, 0); | ||
839 | |||
840 | if (old) { | ||
841 | cancel_delayed_work(&ddata->te_timeout_work); | ||
842 | |||
843 | r = in->ops.dsi->update(in, ddata->channel, dsicm_framedone_cb, | ||
844 | ddata); | ||
845 | if (r) | ||
846 | goto err; | ||
847 | } | ||
848 | |||
849 | return IRQ_HANDLED; | ||
850 | err: | ||
851 | dev_err(&ddata->pdev->dev, "start update failed\n"); | ||
852 | in->ops.dsi->bus_unlock(in); | ||
853 | return IRQ_HANDLED; | ||
854 | } | ||
855 | |||
856 | static void dsicm_te_timeout_work_callback(struct work_struct *work) | ||
857 | { | ||
858 | struct panel_drv_data *ddata = container_of(work, struct panel_drv_data, | ||
859 | te_timeout_work.work); | ||
860 | struct omap_dss_device *in = ddata->in; | ||
861 | |||
862 | dev_err(&ddata->pdev->dev, "TE not received for 250ms!\n"); | ||
863 | |||
864 | atomic_set(&ddata->do_update, 0); | ||
865 | in->ops.dsi->bus_unlock(in); | ||
866 | } | ||
867 | |||
868 | static int dsicm_update(struct omap_dss_device *dssdev, | ||
869 | u16 x, u16 y, u16 w, u16 h) | ||
870 | { | ||
871 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
872 | struct omap_dss_device *in = ddata->in; | ||
873 | int r; | ||
874 | |||
875 | dev_dbg(&ddata->pdev->dev, "update %d, %d, %d x %d\n", x, y, w, h); | ||
876 | |||
877 | mutex_lock(&ddata->lock); | ||
878 | in->ops.dsi->bus_lock(in); | ||
879 | |||
880 | r = dsicm_wake_up(ddata); | ||
881 | if (r) | ||
882 | goto err; | ||
883 | |||
884 | if (!ddata->enabled) { | ||
885 | r = 0; | ||
886 | goto err; | ||
887 | } | ||
888 | |||
889 | /* XXX no need to send this every frame, but dsi break if not done */ | ||
890 | r = dsicm_set_update_window(ddata, 0, 0, | ||
891 | dssdev->panel.timings.x_res, | ||
892 | dssdev->panel.timings.y_res); | ||
893 | if (r) | ||
894 | goto err; | ||
895 | |||
896 | if (ddata->te_enabled && gpio_is_valid(ddata->ext_te_gpio)) { | ||
897 | schedule_delayed_work(&ddata->te_timeout_work, | ||
898 | msecs_to_jiffies(250)); | ||
899 | atomic_set(&ddata->do_update, 1); | ||
900 | } else { | ||
901 | r = in->ops.dsi->update(in, ddata->channel, dsicm_framedone_cb, | ||
902 | ddata); | ||
903 | if (r) | ||
904 | goto err; | ||
905 | } | ||
906 | |||
907 | /* note: no bus_unlock here. unlock is in framedone_cb */ | ||
908 | mutex_unlock(&ddata->lock); | ||
909 | return 0; | ||
910 | err: | ||
911 | in->ops.dsi->bus_unlock(in); | ||
912 | mutex_unlock(&ddata->lock); | ||
913 | return r; | ||
914 | } | ||
915 | |||
916 | static int dsicm_sync(struct omap_dss_device *dssdev) | ||
917 | { | ||
918 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
919 | struct omap_dss_device *in = ddata->in; | ||
920 | |||
921 | dev_dbg(&ddata->pdev->dev, "sync\n"); | ||
922 | |||
923 | mutex_lock(&ddata->lock); | ||
924 | in->ops.dsi->bus_lock(in); | ||
925 | in->ops.dsi->bus_unlock(in); | ||
926 | mutex_unlock(&ddata->lock); | ||
927 | |||
928 | dev_dbg(&ddata->pdev->dev, "sync done\n"); | ||
929 | |||
930 | return 0; | ||
931 | } | ||
932 | |||
933 | static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable) | ||
934 | { | ||
935 | struct omap_dss_device *in = ddata->in; | ||
936 | int r; | ||
937 | |||
938 | if (enable) | ||
939 | r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_TEAR_ON, 0); | ||
940 | else | ||
941 | r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_TEAR_OFF); | ||
942 | |||
943 | if (!gpio_is_valid(ddata->ext_te_gpio)) | ||
944 | in->ops.dsi->enable_te(in, enable); | ||
945 | |||
946 | /* possible panel bug */ | ||
947 | msleep(100); | ||
948 | |||
949 | return r; | ||
950 | } | ||
951 | |||
952 | static int dsicm_enable_te(struct omap_dss_device *dssdev, bool enable) | ||
953 | { | ||
954 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
955 | struct omap_dss_device *in = ddata->in; | ||
956 | int r; | ||
957 | |||
958 | mutex_lock(&ddata->lock); | ||
959 | |||
960 | if (ddata->te_enabled == enable) | ||
961 | goto end; | ||
962 | |||
963 | in->ops.dsi->bus_lock(in); | ||
964 | |||
965 | if (ddata->enabled) { | ||
966 | r = dsicm_wake_up(ddata); | ||
967 | if (r) | ||
968 | goto err; | ||
969 | |||
970 | r = _dsicm_enable_te(ddata, enable); | ||
971 | if (r) | ||
972 | goto err; | ||
973 | } | ||
974 | |||
975 | ddata->te_enabled = enable; | ||
976 | |||
977 | in->ops.dsi->bus_unlock(in); | ||
978 | end: | ||
979 | mutex_unlock(&ddata->lock); | ||
980 | |||
981 | return 0; | ||
982 | err: | ||
983 | in->ops.dsi->bus_unlock(in); | ||
984 | mutex_unlock(&ddata->lock); | ||
985 | |||
986 | return r; | ||
987 | } | ||
988 | |||
989 | static int dsicm_get_te(struct omap_dss_device *dssdev) | ||
990 | { | ||
991 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
992 | int r; | ||
993 | |||
994 | mutex_lock(&ddata->lock); | ||
995 | r = ddata->te_enabled; | ||
996 | mutex_unlock(&ddata->lock); | ||
997 | |||
998 | return r; | ||
999 | } | ||
1000 | |||
1001 | static int dsicm_memory_read(struct omap_dss_device *dssdev, | ||
1002 | void *buf, size_t size, | ||
1003 | u16 x, u16 y, u16 w, u16 h) | ||
1004 | { | ||
1005 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
1006 | struct omap_dss_device *in = ddata->in; | ||
1007 | int r; | ||
1008 | int first = 1; | ||
1009 | int plen; | ||
1010 | unsigned buf_used = 0; | ||
1011 | |||
1012 | if (size < w * h * 3) | ||
1013 | return -ENOMEM; | ||
1014 | |||
1015 | mutex_lock(&ddata->lock); | ||
1016 | |||
1017 | if (!ddata->enabled) { | ||
1018 | r = -ENODEV; | ||
1019 | goto err1; | ||
1020 | } | ||
1021 | |||
1022 | size = min(w * h * 3, | ||
1023 | dssdev->panel.timings.x_res * | ||
1024 | dssdev->panel.timings.y_res * 3); | ||
1025 | |||
1026 | in->ops.dsi->bus_lock(in); | ||
1027 | |||
1028 | r = dsicm_wake_up(ddata); | ||
1029 | if (r) | ||
1030 | goto err2; | ||
1031 | |||
1032 | /* plen 1 or 2 goes into short packet. until checksum error is fixed, | ||
1033 | * use short packets. plen 32 works, but bigger packets seem to cause | ||
1034 | * an error. */ | ||
1035 | if (size % 2) | ||
1036 | plen = 1; | ||
1037 | else | ||
1038 | plen = 2; | ||
1039 | |||
1040 | dsicm_set_update_window(ddata, x, y, w, h); | ||
1041 | |||
1042 | r = in->ops.dsi->set_max_rx_packet_size(in, ddata->channel, plen); | ||
1043 | if (r) | ||
1044 | goto err2; | ||
1045 | |||
1046 | while (buf_used < size) { | ||
1047 | u8 dcs_cmd = first ? 0x2e : 0x3e; | ||
1048 | first = 0; | ||
1049 | |||
1050 | r = in->ops.dsi->dcs_read(in, ddata->channel, dcs_cmd, | ||
1051 | buf + buf_used, size - buf_used); | ||
1052 | |||
1053 | if (r < 0) { | ||
1054 | dev_err(dssdev->dev, "read error\n"); | ||
1055 | goto err3; | ||
1056 | } | ||
1057 | |||
1058 | buf_used += r; | ||
1059 | |||
1060 | if (r < plen) { | ||
1061 | dev_err(&ddata->pdev->dev, "short read\n"); | ||
1062 | break; | ||
1063 | } | ||
1064 | |||
1065 | if (signal_pending(current)) { | ||
1066 | dev_err(&ddata->pdev->dev, "signal pending, " | ||
1067 | "aborting memory read\n"); | ||
1068 | r = -ERESTARTSYS; | ||
1069 | goto err3; | ||
1070 | } | ||
1071 | } | ||
1072 | |||
1073 | r = buf_used; | ||
1074 | |||
1075 | err3: | ||
1076 | in->ops.dsi->set_max_rx_packet_size(in, ddata->channel, 1); | ||
1077 | err2: | ||
1078 | in->ops.dsi->bus_unlock(in); | ||
1079 | err1: | ||
1080 | mutex_unlock(&ddata->lock); | ||
1081 | return r; | ||
1082 | } | ||
1083 | |||
1084 | static void dsicm_ulps_work(struct work_struct *work) | ||
1085 | { | ||
1086 | struct panel_drv_data *ddata = container_of(work, struct panel_drv_data, | ||
1087 | ulps_work.work); | ||
1088 | struct omap_dss_device *dssdev = &ddata->dssdev; | ||
1089 | struct omap_dss_device *in = ddata->in; | ||
1090 | |||
1091 | mutex_lock(&ddata->lock); | ||
1092 | |||
1093 | if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || !ddata->enabled) { | ||
1094 | mutex_unlock(&ddata->lock); | ||
1095 | return; | ||
1096 | } | ||
1097 | |||
1098 | in->ops.dsi->bus_lock(in); | ||
1099 | |||
1100 | dsicm_enter_ulps(ddata); | ||
1101 | |||
1102 | in->ops.dsi->bus_unlock(in); | ||
1103 | mutex_unlock(&ddata->lock); | ||
1104 | } | ||
1105 | |||
1106 | static struct omap_dss_driver dsicm_ops = { | ||
1107 | .connect = dsicm_connect, | ||
1108 | .disconnect = dsicm_disconnect, | ||
1109 | |||
1110 | .enable = dsicm_enable, | ||
1111 | .disable = dsicm_disable, | ||
1112 | |||
1113 | .update = dsicm_update, | ||
1114 | .sync = dsicm_sync, | ||
1115 | |||
1116 | .get_resolution = dsicm_get_resolution, | ||
1117 | .get_recommended_bpp = omapdss_default_get_recommended_bpp, | ||
1118 | |||
1119 | .enable_te = dsicm_enable_te, | ||
1120 | .get_te = dsicm_get_te, | ||
1121 | |||
1122 | .memory_read = dsicm_memory_read, | ||
1123 | }; | ||
1124 | |||
1125 | static int dsicm_probe_pdata(struct platform_device *pdev) | ||
1126 | { | ||
1127 | const struct panel_dsicm_platform_data *pdata; | ||
1128 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
1129 | struct omap_dss_device *dssdev, *in; | ||
1130 | |||
1131 | pdata = dev_get_platdata(&pdev->dev); | ||
1132 | |||
1133 | in = omap_dss_find_output(pdata->source); | ||
1134 | if (in == NULL) { | ||
1135 | dev_err(&pdev->dev, "failed to find video source\n"); | ||
1136 | return -EPROBE_DEFER; | ||
1137 | } | ||
1138 | ddata->in = in; | ||
1139 | |||
1140 | ddata->reset_gpio = pdata->reset_gpio; | ||
1141 | |||
1142 | if (pdata->use_ext_te) | ||
1143 | ddata->ext_te_gpio = pdata->ext_te_gpio; | ||
1144 | else | ||
1145 | ddata->ext_te_gpio = -1; | ||
1146 | |||
1147 | ddata->ulps_timeout = pdata->ulps_timeout; | ||
1148 | |||
1149 | ddata->use_dsi_backlight = pdata->use_dsi_backlight; | ||
1150 | |||
1151 | ddata->pin_config = pdata->pin_config; | ||
1152 | |||
1153 | dssdev = &ddata->dssdev; | ||
1154 | dssdev->name = pdata->name; | ||
1155 | |||
1156 | return 0; | ||
1157 | } | ||
1158 | |||
1159 | static int dsicm_probe(struct platform_device *pdev) | ||
1160 | { | ||
1161 | struct backlight_properties props; | ||
1162 | struct panel_drv_data *ddata; | ||
1163 | struct backlight_device *bldev = NULL; | ||
1164 | struct device *dev = &pdev->dev; | ||
1165 | struct omap_dss_device *dssdev; | ||
1166 | int r; | ||
1167 | |||
1168 | dev_dbg(dev, "probe\n"); | ||
1169 | |||
1170 | ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL); | ||
1171 | if (!ddata) | ||
1172 | return -ENOMEM; | ||
1173 | |||
1174 | platform_set_drvdata(pdev, ddata); | ||
1175 | ddata->pdev = pdev; | ||
1176 | |||
1177 | if (dev_get_platdata(dev)) { | ||
1178 | r = dsicm_probe_pdata(pdev); | ||
1179 | if (r) | ||
1180 | return r; | ||
1181 | } else { | ||
1182 | return -ENODEV; | ||
1183 | } | ||
1184 | |||
1185 | ddata->timings.x_res = 864; | ||
1186 | ddata->timings.y_res = 480; | ||
1187 | ddata->timings.pixel_clock = DIV_ROUND_UP(864 * 480 * 60, 1000); | ||
1188 | |||
1189 | dssdev = &ddata->dssdev; | ||
1190 | dssdev->dev = dev; | ||
1191 | dssdev->driver = &dsicm_ops; | ||
1192 | dssdev->panel.timings = ddata->timings; | ||
1193 | dssdev->type = OMAP_DISPLAY_TYPE_DSI; | ||
1194 | dssdev->owner = THIS_MODULE; | ||
1195 | |||
1196 | dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888; | ||
1197 | dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE | | ||
1198 | OMAP_DSS_DISPLAY_CAP_TEAR_ELIM; | ||
1199 | |||
1200 | r = omapdss_register_display(dssdev); | ||
1201 | if (r) { | ||
1202 | dev_err(dev, "Failed to register panel\n"); | ||
1203 | goto err_reg; | ||
1204 | } | ||
1205 | |||
1206 | mutex_init(&ddata->lock); | ||
1207 | |||
1208 | atomic_set(&ddata->do_update, 0); | ||
1209 | |||
1210 | if (gpio_is_valid(ddata->reset_gpio)) { | ||
1211 | r = devm_gpio_request_one(dev, ddata->reset_gpio, | ||
1212 | GPIOF_OUT_INIT_LOW, "taal rst"); | ||
1213 | if (r) { | ||
1214 | dev_err(dev, "failed to request reset gpio\n"); | ||
1215 | return r; | ||
1216 | } | ||
1217 | } | ||
1218 | |||
1219 | if (gpio_is_valid(ddata->ext_te_gpio)) { | ||
1220 | r = devm_gpio_request_one(dev, ddata->ext_te_gpio, | ||
1221 | GPIOF_IN, "taal irq"); | ||
1222 | if (r) { | ||
1223 | dev_err(dev, "GPIO request failed\n"); | ||
1224 | return r; | ||
1225 | } | ||
1226 | |||
1227 | r = devm_request_irq(dev, gpio_to_irq(ddata->ext_te_gpio), | ||
1228 | dsicm_te_isr, | ||
1229 | IRQF_TRIGGER_RISING, | ||
1230 | "taal vsync", ddata); | ||
1231 | |||
1232 | if (r) { | ||
1233 | dev_err(dev, "IRQ request failed\n"); | ||
1234 | return r; | ||
1235 | } | ||
1236 | |||
1237 | INIT_DEFERRABLE_WORK(&ddata->te_timeout_work, | ||
1238 | dsicm_te_timeout_work_callback); | ||
1239 | |||
1240 | dev_dbg(dev, "Using GPIO TE\n"); | ||
1241 | } | ||
1242 | |||
1243 | ddata->workqueue = create_singlethread_workqueue("dsicm_wq"); | ||
1244 | if (ddata->workqueue == NULL) { | ||
1245 | dev_err(dev, "can't create workqueue\n"); | ||
1246 | return -ENOMEM; | ||
1247 | } | ||
1248 | INIT_DELAYED_WORK(&ddata->ulps_work, dsicm_ulps_work); | ||
1249 | |||
1250 | dsicm_hw_reset(ddata); | ||
1251 | |||
1252 | if (ddata->use_dsi_backlight) { | ||
1253 | memset(&props, 0, sizeof(struct backlight_properties)); | ||
1254 | props.max_brightness = 255; | ||
1255 | |||
1256 | props.type = BACKLIGHT_RAW; | ||
1257 | bldev = backlight_device_register(dev_name(dev), | ||
1258 | dev, ddata, &dsicm_bl_ops, &props); | ||
1259 | if (IS_ERR(bldev)) { | ||
1260 | r = PTR_ERR(bldev); | ||
1261 | goto err_bl; | ||
1262 | } | ||
1263 | |||
1264 | ddata->bldev = bldev; | ||
1265 | |||
1266 | bldev->props.fb_blank = FB_BLANK_UNBLANK; | ||
1267 | bldev->props.power = FB_BLANK_UNBLANK; | ||
1268 | bldev->props.brightness = 255; | ||
1269 | |||
1270 | dsicm_bl_update_status(bldev); | ||
1271 | } | ||
1272 | |||
1273 | r = sysfs_create_group(&dev->kobj, &dsicm_attr_group); | ||
1274 | if (r) { | ||
1275 | dev_err(dev, "failed to create sysfs files\n"); | ||
1276 | goto err_sysfs_create; | ||
1277 | } | ||
1278 | |||
1279 | return 0; | ||
1280 | |||
1281 | err_sysfs_create: | ||
1282 | if (bldev != NULL) | ||
1283 | backlight_device_unregister(bldev); | ||
1284 | err_bl: | ||
1285 | destroy_workqueue(ddata->workqueue); | ||
1286 | err_reg: | ||
1287 | return r; | ||
1288 | } | ||
1289 | |||
1290 | static int __exit dsicm_remove(struct platform_device *pdev) | ||
1291 | { | ||
1292 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
1293 | struct omap_dss_device *dssdev = &ddata->dssdev; | ||
1294 | struct backlight_device *bldev; | ||
1295 | |||
1296 | dev_dbg(&pdev->dev, "remove\n"); | ||
1297 | |||
1298 | omapdss_unregister_display(dssdev); | ||
1299 | |||
1300 | dsicm_disable(dssdev); | ||
1301 | dsicm_disconnect(dssdev); | ||
1302 | |||
1303 | sysfs_remove_group(&pdev->dev.kobj, &dsicm_attr_group); | ||
1304 | |||
1305 | bldev = ddata->bldev; | ||
1306 | if (bldev != NULL) { | ||
1307 | bldev->props.power = FB_BLANK_POWERDOWN; | ||
1308 | dsicm_bl_update_status(bldev); | ||
1309 | backlight_device_unregister(bldev); | ||
1310 | } | ||
1311 | |||
1312 | omap_dss_put_device(ddata->in); | ||
1313 | |||
1314 | dsicm_cancel_ulps_work(ddata); | ||
1315 | destroy_workqueue(ddata->workqueue); | ||
1316 | |||
1317 | /* reset, to be sure that the panel is in a valid state */ | ||
1318 | dsicm_hw_reset(ddata); | ||
1319 | |||
1320 | return 0; | ||
1321 | } | ||
1322 | |||
1323 | static struct platform_driver dsicm_driver = { | ||
1324 | .probe = dsicm_probe, | ||
1325 | .remove = __exit_p(dsicm_remove), | ||
1326 | .driver = { | ||
1327 | .name = "panel-dsi-cm", | ||
1328 | .owner = THIS_MODULE, | ||
1329 | }, | ||
1330 | }; | ||
1331 | |||
1332 | module_platform_driver(dsicm_driver); | ||
1333 | |||
1334 | MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); | ||
1335 | MODULE_DESCRIPTION("Generic DSI Command Mode Panel Driver"); | ||
1336 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/omap2/displays-new/panel-lgphilips-lb035q02.c b/drivers/video/omap2/displays-new/panel-lgphilips-lb035q02.c new file mode 100644 index 000000000000..6e8977b18950 --- /dev/null +++ b/drivers/video/omap2/displays-new/panel-lgphilips-lb035q02.c | |||
@@ -0,0 +1,358 @@ | |||
1 | /* | ||
2 | * LG.Philips LB035Q02 LCD Panel driver | ||
3 | * | ||
4 | * Copyright (C) 2013 Texas Instruments | ||
5 | * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> | ||
6 | * Based on a driver by: Steve Sakoman <steve@sakoman.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify it | ||
9 | * under the terms of the GNU General Public License version 2 as published by | ||
10 | * the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/delay.h> | ||
15 | #include <linux/spi/spi.h> | ||
16 | #include <linux/mutex.h> | ||
17 | #include <linux/gpio.h> | ||
18 | |||
19 | #include <video/omapdss.h> | ||
20 | #include <video/omap-panel-data.h> | ||
21 | |||
22 | static struct omap_video_timings lb035q02_timings = { | ||
23 | .x_res = 320, | ||
24 | .y_res = 240, | ||
25 | |||
26 | .pixel_clock = 6500, | ||
27 | |||
28 | .hsw = 2, | ||
29 | .hfp = 20, | ||
30 | .hbp = 68, | ||
31 | |||
32 | .vsw = 2, | ||
33 | .vfp = 4, | ||
34 | .vbp = 18, | ||
35 | |||
36 | .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, | ||
37 | .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, | ||
38 | .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, | ||
39 | .de_level = OMAPDSS_SIG_ACTIVE_HIGH, | ||
40 | .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, | ||
41 | }; | ||
42 | |||
43 | struct panel_drv_data { | ||
44 | struct omap_dss_device dssdev; | ||
45 | struct omap_dss_device *in; | ||
46 | |||
47 | struct spi_device *spi; | ||
48 | |||
49 | int data_lines; | ||
50 | |||
51 | struct omap_video_timings videomode; | ||
52 | |||
53 | int reset_gpio; | ||
54 | int backlight_gpio; | ||
55 | int enable_gpio; | ||
56 | }; | ||
57 | |||
58 | #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) | ||
59 | |||
60 | static int lb035q02_write_reg(struct spi_device *spi, u8 reg, u16 val) | ||
61 | { | ||
62 | struct spi_message msg; | ||
63 | struct spi_transfer index_xfer = { | ||
64 | .len = 3, | ||
65 | .cs_change = 1, | ||
66 | }; | ||
67 | struct spi_transfer value_xfer = { | ||
68 | .len = 3, | ||
69 | }; | ||
70 | u8 buffer[16]; | ||
71 | |||
72 | spi_message_init(&msg); | ||
73 | |||
74 | /* register index */ | ||
75 | buffer[0] = 0x70; | ||
76 | buffer[1] = 0x00; | ||
77 | buffer[2] = reg & 0x7f; | ||
78 | index_xfer.tx_buf = buffer; | ||
79 | spi_message_add_tail(&index_xfer, &msg); | ||
80 | |||
81 | /* register value */ | ||
82 | buffer[4] = 0x72; | ||
83 | buffer[5] = val >> 8; | ||
84 | buffer[6] = val; | ||
85 | value_xfer.tx_buf = buffer + 4; | ||
86 | spi_message_add_tail(&value_xfer, &msg); | ||
87 | |||
88 | return spi_sync(spi, &msg); | ||
89 | } | ||
90 | |||
91 | static void init_lb035q02_panel(struct spi_device *spi) | ||
92 | { | ||
93 | /* Init sequence from page 28 of the lb035q02 spec */ | ||
94 | lb035q02_write_reg(spi, 0x01, 0x6300); | ||
95 | lb035q02_write_reg(spi, 0x02, 0x0200); | ||
96 | lb035q02_write_reg(spi, 0x03, 0x0177); | ||
97 | lb035q02_write_reg(spi, 0x04, 0x04c7); | ||
98 | lb035q02_write_reg(spi, 0x05, 0xffc0); | ||
99 | lb035q02_write_reg(spi, 0x06, 0xe806); | ||
100 | lb035q02_write_reg(spi, 0x0a, 0x4008); | ||
101 | lb035q02_write_reg(spi, 0x0b, 0x0000); | ||
102 | lb035q02_write_reg(spi, 0x0d, 0x0030); | ||
103 | lb035q02_write_reg(spi, 0x0e, 0x2800); | ||
104 | lb035q02_write_reg(spi, 0x0f, 0x0000); | ||
105 | lb035q02_write_reg(spi, 0x16, 0x9f80); | ||
106 | lb035q02_write_reg(spi, 0x17, 0x0a0f); | ||
107 | lb035q02_write_reg(spi, 0x1e, 0x00c1); | ||
108 | lb035q02_write_reg(spi, 0x30, 0x0300); | ||
109 | lb035q02_write_reg(spi, 0x31, 0x0007); | ||
110 | lb035q02_write_reg(spi, 0x32, 0x0000); | ||
111 | lb035q02_write_reg(spi, 0x33, 0x0000); | ||
112 | lb035q02_write_reg(spi, 0x34, 0x0707); | ||
113 | lb035q02_write_reg(spi, 0x35, 0x0004); | ||
114 | lb035q02_write_reg(spi, 0x36, 0x0302); | ||
115 | lb035q02_write_reg(spi, 0x37, 0x0202); | ||
116 | lb035q02_write_reg(spi, 0x3a, 0x0a0d); | ||
117 | lb035q02_write_reg(spi, 0x3b, 0x0806); | ||
118 | } | ||
119 | |||
120 | static int lb035q02_connect(struct omap_dss_device *dssdev) | ||
121 | { | ||
122 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
123 | struct omap_dss_device *in = ddata->in; | ||
124 | int r; | ||
125 | |||
126 | if (omapdss_device_is_connected(dssdev)) | ||
127 | return 0; | ||
128 | |||
129 | r = in->ops.dpi->connect(in, dssdev); | ||
130 | if (r) | ||
131 | return r; | ||
132 | |||
133 | init_lb035q02_panel(ddata->spi); | ||
134 | |||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static void lb035q02_disconnect(struct omap_dss_device *dssdev) | ||
139 | { | ||
140 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
141 | struct omap_dss_device *in = ddata->in; | ||
142 | |||
143 | if (!omapdss_device_is_connected(dssdev)) | ||
144 | return; | ||
145 | |||
146 | in->ops.dpi->disconnect(in, dssdev); | ||
147 | } | ||
148 | |||
149 | static int lb035q02_enable(struct omap_dss_device *dssdev) | ||
150 | { | ||
151 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
152 | struct omap_dss_device *in = ddata->in; | ||
153 | int r; | ||
154 | |||
155 | if (!omapdss_device_is_connected(dssdev)) | ||
156 | return -ENODEV; | ||
157 | |||
158 | if (omapdss_device_is_enabled(dssdev)) | ||
159 | return 0; | ||
160 | |||
161 | in->ops.dpi->set_data_lines(in, ddata->data_lines); | ||
162 | in->ops.dpi->set_timings(in, &ddata->videomode); | ||
163 | |||
164 | r = in->ops.dpi->enable(in); | ||
165 | if (r) | ||
166 | return r; | ||
167 | |||
168 | if (gpio_is_valid(ddata->enable_gpio)) | ||
169 | gpio_set_value_cansleep(ddata->enable_gpio, 1); | ||
170 | |||
171 | if (gpio_is_valid(ddata->backlight_gpio)) | ||
172 | gpio_set_value_cansleep(ddata->backlight_gpio, 1); | ||
173 | |||
174 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | static void lb035q02_disable(struct omap_dss_device *dssdev) | ||
180 | { | ||
181 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
182 | struct omap_dss_device *in = ddata->in; | ||
183 | |||
184 | if (!omapdss_device_is_enabled(dssdev)) | ||
185 | return; | ||
186 | |||
187 | if (gpio_is_valid(ddata->enable_gpio)) | ||
188 | gpio_set_value_cansleep(ddata->enable_gpio, 0); | ||
189 | |||
190 | if (gpio_is_valid(ddata->backlight_gpio)) | ||
191 | gpio_set_value_cansleep(ddata->backlight_gpio, 0); | ||
192 | |||
193 | in->ops.dpi->disable(in); | ||
194 | |||
195 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; | ||
196 | } | ||
197 | |||
198 | static void lb035q02_set_timings(struct omap_dss_device *dssdev, | ||
199 | struct omap_video_timings *timings) | ||
200 | { | ||
201 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
202 | struct omap_dss_device *in = ddata->in; | ||
203 | |||
204 | ddata->videomode = *timings; | ||
205 | dssdev->panel.timings = *timings; | ||
206 | |||
207 | in->ops.dpi->set_timings(in, timings); | ||
208 | } | ||
209 | |||
210 | static void lb035q02_get_timings(struct omap_dss_device *dssdev, | ||
211 | struct omap_video_timings *timings) | ||
212 | { | ||
213 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
214 | |||
215 | *timings = ddata->videomode; | ||
216 | } | ||
217 | |||
218 | static int lb035q02_check_timings(struct omap_dss_device *dssdev, | ||
219 | struct omap_video_timings *timings) | ||
220 | { | ||
221 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
222 | struct omap_dss_device *in = ddata->in; | ||
223 | |||
224 | return in->ops.dpi->check_timings(in, timings); | ||
225 | } | ||
226 | |||
227 | static struct omap_dss_driver lb035q02_ops = { | ||
228 | .connect = lb035q02_connect, | ||
229 | .disconnect = lb035q02_disconnect, | ||
230 | |||
231 | .enable = lb035q02_enable, | ||
232 | .disable = lb035q02_disable, | ||
233 | |||
234 | .set_timings = lb035q02_set_timings, | ||
235 | .get_timings = lb035q02_get_timings, | ||
236 | .check_timings = lb035q02_check_timings, | ||
237 | |||
238 | .get_resolution = omapdss_default_get_resolution, | ||
239 | }; | ||
240 | |||
241 | static int lb035q02_probe_pdata(struct spi_device *spi) | ||
242 | { | ||
243 | const struct panel_lb035q02_platform_data *pdata; | ||
244 | struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); | ||
245 | struct omap_dss_device *dssdev, *in; | ||
246 | |||
247 | pdata = dev_get_platdata(&spi->dev); | ||
248 | |||
249 | in = omap_dss_find_output(pdata->source); | ||
250 | if (in == NULL) { | ||
251 | dev_err(&spi->dev, "failed to find video source '%s'\n", | ||
252 | pdata->source); | ||
253 | return -EPROBE_DEFER; | ||
254 | } | ||
255 | |||
256 | ddata->in = in; | ||
257 | |||
258 | ddata->data_lines = pdata->data_lines; | ||
259 | |||
260 | dssdev = &ddata->dssdev; | ||
261 | dssdev->name = pdata->name; | ||
262 | |||
263 | ddata->enable_gpio = pdata->enable_gpio; | ||
264 | ddata->backlight_gpio = pdata->backlight_gpio; | ||
265 | |||
266 | return 0; | ||
267 | } | ||
268 | |||
269 | static int lb035q02_panel_spi_probe(struct spi_device *spi) | ||
270 | { | ||
271 | struct panel_drv_data *ddata; | ||
272 | struct omap_dss_device *dssdev; | ||
273 | int r; | ||
274 | |||
275 | ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL); | ||
276 | if (ddata == NULL) | ||
277 | return -ENOMEM; | ||
278 | |||
279 | dev_set_drvdata(&spi->dev, ddata); | ||
280 | |||
281 | ddata->spi = spi; | ||
282 | |||
283 | if (dev_get_platdata(&spi->dev)) { | ||
284 | r = lb035q02_probe_pdata(spi); | ||
285 | if (r) | ||
286 | return r; | ||
287 | } else { | ||
288 | return -ENODEV; | ||
289 | } | ||
290 | |||
291 | if (gpio_is_valid(ddata->enable_gpio)) { | ||
292 | r = devm_gpio_request_one(&spi->dev, ddata->enable_gpio, | ||
293 | GPIOF_OUT_INIT_LOW, "panel enable"); | ||
294 | if (r) | ||
295 | goto err_gpio; | ||
296 | } | ||
297 | |||
298 | if (gpio_is_valid(ddata->backlight_gpio)) { | ||
299 | r = devm_gpio_request_one(&spi->dev, ddata->backlight_gpio, | ||
300 | GPIOF_OUT_INIT_LOW, "panel backlight"); | ||
301 | if (r) | ||
302 | goto err_gpio; | ||
303 | } | ||
304 | |||
305 | ddata->videomode = lb035q02_timings; | ||
306 | |||
307 | dssdev = &ddata->dssdev; | ||
308 | dssdev->dev = &spi->dev; | ||
309 | dssdev->driver = &lb035q02_ops; | ||
310 | dssdev->type = OMAP_DISPLAY_TYPE_DPI; | ||
311 | dssdev->owner = THIS_MODULE; | ||
312 | dssdev->panel.timings = ddata->videomode; | ||
313 | dssdev->phy.dpi.data_lines = ddata->data_lines; | ||
314 | |||
315 | r = omapdss_register_display(dssdev); | ||
316 | if (r) { | ||
317 | dev_err(&spi->dev, "Failed to register panel\n"); | ||
318 | goto err_reg; | ||
319 | } | ||
320 | |||
321 | return 0; | ||
322 | |||
323 | err_reg: | ||
324 | err_gpio: | ||
325 | omap_dss_put_device(ddata->in); | ||
326 | return r; | ||
327 | } | ||
328 | |||
329 | static int lb035q02_panel_spi_remove(struct spi_device *spi) | ||
330 | { | ||
331 | struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); | ||
332 | struct omap_dss_device *dssdev = &ddata->dssdev; | ||
333 | struct omap_dss_device *in = ddata->in; | ||
334 | |||
335 | omapdss_unregister_display(dssdev); | ||
336 | |||
337 | lb035q02_disable(dssdev); | ||
338 | lb035q02_disconnect(dssdev); | ||
339 | |||
340 | omap_dss_put_device(in); | ||
341 | |||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | static struct spi_driver lb035q02_spi_driver = { | ||
346 | .probe = lb035q02_panel_spi_probe, | ||
347 | .remove = lb035q02_panel_spi_remove, | ||
348 | .driver = { | ||
349 | .name = "panel_lgphilips_lb035q02", | ||
350 | .owner = THIS_MODULE, | ||
351 | }, | ||
352 | }; | ||
353 | |||
354 | module_spi_driver(lb035q02_spi_driver); | ||
355 | |||
356 | MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); | ||
357 | MODULE_DESCRIPTION("LG.Philips LB035Q02 LCD Panel driver"); | ||
358 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/omap2/displays-new/panel-nec-nl8048hl11.c b/drivers/video/omap2/displays-new/panel-nec-nl8048hl11.c new file mode 100644 index 000000000000..bb217da65c5f --- /dev/null +++ b/drivers/video/omap2/displays-new/panel-nec-nl8048hl11.c | |||
@@ -0,0 +1,394 @@ | |||
1 | /* | ||
2 | * NEC NL8048HL11 Panel driver | ||
3 | * | ||
4 | * Copyright (C) 2010 Texas Instruments Inc. | ||
5 | * Author: Erik Gilling <konkers@android.com> | ||
6 | * Converted to new DSS device model: Tomi Valkeinen <tomi.valkeinen@ti.com> | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License as published by | ||
10 | * the Free Software Foundation; either version 2 of the License, or | ||
11 | * (at your option) any later version. | ||
12 | */ | ||
13 | |||
14 | #include <linux/module.h> | ||
15 | #include <linux/delay.h> | ||
16 | #include <linux/spi/spi.h> | ||
17 | #include <linux/fb.h> | ||
18 | #include <linux/gpio.h> | ||
19 | |||
20 | #include <video/omapdss.h> | ||
21 | #include <video/omap-panel-data.h> | ||
22 | |||
23 | struct panel_drv_data { | ||
24 | struct omap_dss_device dssdev; | ||
25 | struct omap_dss_device *in; | ||
26 | |||
27 | struct omap_video_timings videomode; | ||
28 | |||
29 | int data_lines; | ||
30 | |||
31 | int res_gpio; | ||
32 | int qvga_gpio; | ||
33 | |||
34 | struct spi_device *spi; | ||
35 | }; | ||
36 | |||
37 | #define LCD_XRES 800 | ||
38 | #define LCD_YRES 480 | ||
39 | /* | ||
40 | * NEC PIX Clock Ratings | ||
41 | * MIN:21.8MHz TYP:23.8MHz MAX:25.7MHz | ||
42 | */ | ||
43 | #define LCD_PIXEL_CLOCK 23800 | ||
44 | |||
45 | static const struct { | ||
46 | unsigned char addr; | ||
47 | unsigned char dat; | ||
48 | } nec_8048_init_seq[] = { | ||
49 | { 3, 0x01 }, { 0, 0x00 }, { 1, 0x01 }, { 4, 0x00 }, { 5, 0x14 }, | ||
50 | { 6, 0x24 }, { 16, 0xD7 }, { 17, 0x00 }, { 18, 0x00 }, { 19, 0x55 }, | ||
51 | { 20, 0x01 }, { 21, 0x70 }, { 22, 0x1E }, { 23, 0x25 }, { 24, 0x25 }, | ||
52 | { 25, 0x02 }, { 26, 0x02 }, { 27, 0xA0 }, { 32, 0x2F }, { 33, 0x0F }, | ||
53 | { 34, 0x0F }, { 35, 0x0F }, { 36, 0x0F }, { 37, 0x0F }, { 38, 0x0F }, | ||
54 | { 39, 0x00 }, { 40, 0x02 }, { 41, 0x02 }, { 42, 0x02 }, { 43, 0x0F }, | ||
55 | { 44, 0x0F }, { 45, 0x0F }, { 46, 0x0F }, { 47, 0x0F }, { 48, 0x0F }, | ||
56 | { 49, 0x0F }, { 50, 0x00 }, { 51, 0x02 }, { 52, 0x02 }, { 53, 0x02 }, | ||
57 | { 80, 0x0C }, { 83, 0x42 }, { 84, 0x42 }, { 85, 0x41 }, { 86, 0x14 }, | ||
58 | { 89, 0x88 }, { 90, 0x01 }, { 91, 0x00 }, { 92, 0x02 }, { 93, 0x0C }, | ||
59 | { 94, 0x1C }, { 95, 0x27 }, { 98, 0x49 }, { 99, 0x27 }, { 102, 0x76 }, | ||
60 | { 103, 0x27 }, { 112, 0x01 }, { 113, 0x0E }, { 114, 0x02 }, | ||
61 | { 115, 0x0C }, { 118, 0x0C }, { 121, 0x30 }, { 130, 0x00 }, | ||
62 | { 131, 0x00 }, { 132, 0xFC }, { 134, 0x00 }, { 136, 0x00 }, | ||
63 | { 138, 0x00 }, { 139, 0x00 }, { 140, 0x00 }, { 141, 0xFC }, | ||
64 | { 143, 0x00 }, { 145, 0x00 }, { 147, 0x00 }, { 148, 0x00 }, | ||
65 | { 149, 0x00 }, { 150, 0xFC }, { 152, 0x00 }, { 154, 0x00 }, | ||
66 | { 156, 0x00 }, { 157, 0x00 }, { 2, 0x00 }, | ||
67 | }; | ||
68 | |||
69 | static const struct omap_video_timings nec_8048_panel_timings = { | ||
70 | .x_res = LCD_XRES, | ||
71 | .y_res = LCD_YRES, | ||
72 | .pixel_clock = LCD_PIXEL_CLOCK, | ||
73 | .hfp = 6, | ||
74 | .hsw = 1, | ||
75 | .hbp = 4, | ||
76 | .vfp = 3, | ||
77 | .vsw = 1, | ||
78 | .vbp = 4, | ||
79 | |||
80 | .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, | ||
81 | .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, | ||
82 | .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, | ||
83 | .de_level = OMAPDSS_SIG_ACTIVE_HIGH, | ||
84 | .sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, | ||
85 | }; | ||
86 | |||
87 | #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) | ||
88 | |||
89 | static int nec_8048_spi_send(struct spi_device *spi, unsigned char reg_addr, | ||
90 | unsigned char reg_data) | ||
91 | { | ||
92 | int ret = 0; | ||
93 | unsigned int cmd = 0, data = 0; | ||
94 | |||
95 | cmd = 0x0000 | reg_addr; /* register address write */ | ||
96 | data = 0x0100 | reg_data; /* register data write */ | ||
97 | data = (cmd << 16) | data; | ||
98 | |||
99 | ret = spi_write(spi, (unsigned char *)&data, 4); | ||
100 | if (ret) | ||
101 | pr_err("error in spi_write %x\n", data); | ||
102 | |||
103 | return ret; | ||
104 | } | ||
105 | |||
106 | static int init_nec_8048_wvga_lcd(struct spi_device *spi) | ||
107 | { | ||
108 | unsigned int i; | ||
109 | /* Initialization Sequence */ | ||
110 | /* nec_8048_spi_send(spi, REG, VAL) */ | ||
111 | for (i = 0; i < (ARRAY_SIZE(nec_8048_init_seq) - 1); i++) | ||
112 | nec_8048_spi_send(spi, nec_8048_init_seq[i].addr, | ||
113 | nec_8048_init_seq[i].dat); | ||
114 | udelay(20); | ||
115 | nec_8048_spi_send(spi, nec_8048_init_seq[i].addr, | ||
116 | nec_8048_init_seq[i].dat); | ||
117 | return 0; | ||
118 | } | ||
119 | |||
120 | static int nec_8048_connect(struct omap_dss_device *dssdev) | ||
121 | { | ||
122 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
123 | struct omap_dss_device *in = ddata->in; | ||
124 | int r; | ||
125 | |||
126 | if (omapdss_device_is_connected(dssdev)) | ||
127 | return 0; | ||
128 | |||
129 | r = in->ops.dpi->connect(in, dssdev); | ||
130 | if (r) | ||
131 | return r; | ||
132 | |||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | static void nec_8048_disconnect(struct omap_dss_device *dssdev) | ||
137 | { | ||
138 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
139 | struct omap_dss_device *in = ddata->in; | ||
140 | |||
141 | if (!omapdss_device_is_connected(dssdev)) | ||
142 | return; | ||
143 | |||
144 | in->ops.dpi->disconnect(in, dssdev); | ||
145 | } | ||
146 | |||
147 | static int nec_8048_enable(struct omap_dss_device *dssdev) | ||
148 | { | ||
149 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
150 | struct omap_dss_device *in = ddata->in; | ||
151 | int r; | ||
152 | |||
153 | if (!omapdss_device_is_connected(dssdev)) | ||
154 | return -ENODEV; | ||
155 | |||
156 | if (omapdss_device_is_enabled(dssdev)) | ||
157 | return 0; | ||
158 | |||
159 | in->ops.dpi->set_data_lines(in, ddata->data_lines); | ||
160 | in->ops.dpi->set_timings(in, &ddata->videomode); | ||
161 | |||
162 | r = in->ops.dpi->enable(in); | ||
163 | if (r) | ||
164 | return r; | ||
165 | |||
166 | if (gpio_is_valid(ddata->res_gpio)) | ||
167 | gpio_set_value_cansleep(ddata->res_gpio, 1); | ||
168 | |||
169 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | ||
170 | |||
171 | return 0; | ||
172 | } | ||
173 | |||
174 | static void nec_8048_disable(struct omap_dss_device *dssdev) | ||
175 | { | ||
176 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
177 | struct omap_dss_device *in = ddata->in; | ||
178 | |||
179 | if (!omapdss_device_is_enabled(dssdev)) | ||
180 | return; | ||
181 | |||
182 | if (gpio_is_valid(ddata->res_gpio)) | ||
183 | gpio_set_value_cansleep(ddata->res_gpio, 0); | ||
184 | |||
185 | in->ops.dpi->disable(in); | ||
186 | |||
187 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; | ||
188 | } | ||
189 | |||
190 | static void nec_8048_set_timings(struct omap_dss_device *dssdev, | ||
191 | struct omap_video_timings *timings) | ||
192 | { | ||
193 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
194 | struct omap_dss_device *in = ddata->in; | ||
195 | |||
196 | ddata->videomode = *timings; | ||
197 | dssdev->panel.timings = *timings; | ||
198 | |||
199 | in->ops.dpi->set_timings(in, timings); | ||
200 | } | ||
201 | |||
202 | static void nec_8048_get_timings(struct omap_dss_device *dssdev, | ||
203 | struct omap_video_timings *timings) | ||
204 | { | ||
205 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
206 | |||
207 | *timings = ddata->videomode; | ||
208 | } | ||
209 | |||
210 | static int nec_8048_check_timings(struct omap_dss_device *dssdev, | ||
211 | struct omap_video_timings *timings) | ||
212 | { | ||
213 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
214 | struct omap_dss_device *in = ddata->in; | ||
215 | |||
216 | return in->ops.dpi->check_timings(in, timings); | ||
217 | } | ||
218 | |||
219 | static struct omap_dss_driver nec_8048_ops = { | ||
220 | .connect = nec_8048_connect, | ||
221 | .disconnect = nec_8048_disconnect, | ||
222 | |||
223 | .enable = nec_8048_enable, | ||
224 | .disable = nec_8048_disable, | ||
225 | |||
226 | .set_timings = nec_8048_set_timings, | ||
227 | .get_timings = nec_8048_get_timings, | ||
228 | .check_timings = nec_8048_check_timings, | ||
229 | |||
230 | .get_resolution = omapdss_default_get_resolution, | ||
231 | }; | ||
232 | |||
233 | |||
234 | static int nec_8048_probe_pdata(struct spi_device *spi) | ||
235 | { | ||
236 | const struct panel_nec_nl8048hl11_platform_data *pdata; | ||
237 | struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); | ||
238 | struct omap_dss_device *dssdev, *in; | ||
239 | |||
240 | pdata = dev_get_platdata(&spi->dev); | ||
241 | |||
242 | ddata->qvga_gpio = pdata->qvga_gpio; | ||
243 | ddata->res_gpio = pdata->res_gpio; | ||
244 | |||
245 | in = omap_dss_find_output(pdata->source); | ||
246 | if (in == NULL) { | ||
247 | dev_err(&spi->dev, "failed to find video source '%s'\n", | ||
248 | pdata->source); | ||
249 | return -EPROBE_DEFER; | ||
250 | } | ||
251 | ddata->in = in; | ||
252 | |||
253 | ddata->data_lines = pdata->data_lines; | ||
254 | |||
255 | dssdev = &ddata->dssdev; | ||
256 | dssdev->name = pdata->name; | ||
257 | |||
258 | return 0; | ||
259 | } | ||
260 | |||
261 | static int nec_8048_probe(struct spi_device *spi) | ||
262 | { | ||
263 | struct panel_drv_data *ddata; | ||
264 | struct omap_dss_device *dssdev; | ||
265 | int r; | ||
266 | |||
267 | dev_dbg(&spi->dev, "%s\n", __func__); | ||
268 | |||
269 | spi->mode = SPI_MODE_0; | ||
270 | spi->bits_per_word = 32; | ||
271 | |||
272 | r = spi_setup(spi); | ||
273 | if (r < 0) { | ||
274 | dev_err(&spi->dev, "spi_setup failed: %d\n", r); | ||
275 | return r; | ||
276 | } | ||
277 | |||
278 | init_nec_8048_wvga_lcd(spi); | ||
279 | |||
280 | ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL); | ||
281 | if (ddata == NULL) | ||
282 | return -ENOMEM; | ||
283 | |||
284 | dev_set_drvdata(&spi->dev, ddata); | ||
285 | |||
286 | ddata->spi = spi; | ||
287 | |||
288 | if (dev_get_platdata(&spi->dev)) { | ||
289 | r = nec_8048_probe_pdata(spi); | ||
290 | if (r) | ||
291 | return r; | ||
292 | } else { | ||
293 | return -ENODEV; | ||
294 | } | ||
295 | |||
296 | if (gpio_is_valid(ddata->qvga_gpio)) { | ||
297 | r = devm_gpio_request_one(&spi->dev, ddata->qvga_gpio, | ||
298 | GPIOF_OUT_INIT_HIGH, "lcd QVGA"); | ||
299 | if (r) | ||
300 | goto err_gpio; | ||
301 | } | ||
302 | |||
303 | if (gpio_is_valid(ddata->res_gpio)) { | ||
304 | r = devm_gpio_request_one(&spi->dev, ddata->res_gpio, | ||
305 | GPIOF_OUT_INIT_LOW, "lcd RES"); | ||
306 | if (r) | ||
307 | goto err_gpio; | ||
308 | } | ||
309 | |||
310 | ddata->videomode = nec_8048_panel_timings; | ||
311 | |||
312 | dssdev = &ddata->dssdev; | ||
313 | dssdev->dev = &spi->dev; | ||
314 | dssdev->driver = &nec_8048_ops; | ||
315 | dssdev->type = OMAP_DISPLAY_TYPE_DPI; | ||
316 | dssdev->owner = THIS_MODULE; | ||
317 | dssdev->panel.timings = ddata->videomode; | ||
318 | |||
319 | r = omapdss_register_display(dssdev); | ||
320 | if (r) { | ||
321 | dev_err(&spi->dev, "Failed to register panel\n"); | ||
322 | goto err_reg; | ||
323 | } | ||
324 | |||
325 | return 0; | ||
326 | |||
327 | err_reg: | ||
328 | err_gpio: | ||
329 | omap_dss_put_device(ddata->in); | ||
330 | return r; | ||
331 | } | ||
332 | |||
333 | static int nec_8048_remove(struct spi_device *spi) | ||
334 | { | ||
335 | struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); | ||
336 | struct omap_dss_device *dssdev = &ddata->dssdev; | ||
337 | struct omap_dss_device *in = ddata->in; | ||
338 | |||
339 | dev_dbg(&ddata->spi->dev, "%s\n", __func__); | ||
340 | |||
341 | omapdss_unregister_display(dssdev); | ||
342 | |||
343 | nec_8048_disable(dssdev); | ||
344 | nec_8048_disconnect(dssdev); | ||
345 | |||
346 | omap_dss_put_device(in); | ||
347 | |||
348 | return 0; | ||
349 | } | ||
350 | |||
351 | #ifdef CONFIG_PM_SLEEP | ||
352 | static int nec_8048_suspend(struct device *dev) | ||
353 | { | ||
354 | struct spi_device *spi = to_spi_device(dev); | ||
355 | |||
356 | nec_8048_spi_send(spi, 2, 0x01); | ||
357 | mdelay(40); | ||
358 | |||
359 | return 0; | ||
360 | } | ||
361 | |||
362 | static int nec_8048_resume(struct device *dev) | ||
363 | { | ||
364 | struct spi_device *spi = to_spi_device(dev); | ||
365 | |||
366 | /* reinitialize the panel */ | ||
367 | spi_setup(spi); | ||
368 | nec_8048_spi_send(spi, 2, 0x00); | ||
369 | init_nec_8048_wvga_lcd(spi); | ||
370 | |||
371 | return 0; | ||
372 | } | ||
373 | static SIMPLE_DEV_PM_OPS(nec_8048_pm_ops, nec_8048_suspend, | ||
374 | nec_8048_resume); | ||
375 | #define NEC_8048_PM_OPS (&nec_8048_pm_ops) | ||
376 | #else | ||
377 | #define NEC_8048_PM_OPS NULL | ||
378 | #endif | ||
379 | |||
380 | static struct spi_driver nec_8048_driver = { | ||
381 | .driver = { | ||
382 | .name = "panel-nec-nl8048hl11", | ||
383 | .owner = THIS_MODULE, | ||
384 | .pm = NEC_8048_PM_OPS, | ||
385 | }, | ||
386 | .probe = nec_8048_probe, | ||
387 | .remove = nec_8048_remove, | ||
388 | }; | ||
389 | |||
390 | module_spi_driver(nec_8048_driver); | ||
391 | |||
392 | MODULE_AUTHOR("Erik Gilling <konkers@android.com>"); | ||
393 | MODULE_DESCRIPTION("NEC-NL8048HL11 Driver"); | ||
394 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/omap2/displays-new/panel-sharp-ls037v7dw01.c b/drivers/video/omap2/displays-new/panel-sharp-ls037v7dw01.c new file mode 100644 index 000000000000..72a4fb5aa6b1 --- /dev/null +++ b/drivers/video/omap2/displays-new/panel-sharp-ls037v7dw01.c | |||
@@ -0,0 +1,324 @@ | |||
1 | /* | ||
2 | * LCD panel driver for Sharp LS037V7DW01 | ||
3 | * | ||
4 | * Copyright (C) 2013 Texas Instruments | ||
5 | * Author: Tomi Valkeinen <tomi.valkeinen@ti.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify it | ||
8 | * under the terms of the GNU General Public License version 2 as published by | ||
9 | * the Free Software Foundation. | ||
10 | */ | ||
11 | |||
12 | #include <linux/delay.h> | ||
13 | #include <linux/gpio.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/platform_device.h> | ||
16 | #include <linux/slab.h> | ||
17 | |||
18 | #include <video/omapdss.h> | ||
19 | #include <video/omap-panel-data.h> | ||
20 | |||
21 | struct panel_drv_data { | ||
22 | struct omap_dss_device dssdev; | ||
23 | struct omap_dss_device *in; | ||
24 | |||
25 | int data_lines; | ||
26 | |||
27 | struct omap_video_timings videomode; | ||
28 | |||
29 | int resb_gpio; | ||
30 | int ini_gpio; | ||
31 | int mo_gpio; | ||
32 | int lr_gpio; | ||
33 | int ud_gpio; | ||
34 | }; | ||
35 | |||
36 | static const struct omap_video_timings sharp_ls_timings = { | ||
37 | .x_res = 480, | ||
38 | .y_res = 640, | ||
39 | |||
40 | .pixel_clock = 19200, | ||
41 | |||
42 | .hsw = 2, | ||
43 | .hfp = 1, | ||
44 | .hbp = 28, | ||
45 | |||
46 | .vsw = 1, | ||
47 | .vfp = 1, | ||
48 | .vbp = 1, | ||
49 | |||
50 | .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, | ||
51 | .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, | ||
52 | .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, | ||
53 | .de_level = OMAPDSS_SIG_ACTIVE_HIGH, | ||
54 | .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, | ||
55 | }; | ||
56 | |||
57 | #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) | ||
58 | |||
59 | static int sharp_ls_connect(struct omap_dss_device *dssdev) | ||
60 | { | ||
61 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
62 | struct omap_dss_device *in = ddata->in; | ||
63 | int r; | ||
64 | |||
65 | if (omapdss_device_is_connected(dssdev)) | ||
66 | return 0; | ||
67 | |||
68 | r = in->ops.dpi->connect(in, dssdev); | ||
69 | if (r) | ||
70 | return r; | ||
71 | |||
72 | return 0; | ||
73 | } | ||
74 | |||
75 | static void sharp_ls_disconnect(struct omap_dss_device *dssdev) | ||
76 | { | ||
77 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
78 | struct omap_dss_device *in = ddata->in; | ||
79 | |||
80 | if (!omapdss_device_is_connected(dssdev)) | ||
81 | return; | ||
82 | |||
83 | in->ops.dpi->disconnect(in, dssdev); | ||
84 | } | ||
85 | |||
86 | static int sharp_ls_enable(struct omap_dss_device *dssdev) | ||
87 | { | ||
88 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
89 | struct omap_dss_device *in = ddata->in; | ||
90 | int r; | ||
91 | |||
92 | if (!omapdss_device_is_connected(dssdev)) | ||
93 | return -ENODEV; | ||
94 | |||
95 | if (omapdss_device_is_enabled(dssdev)) | ||
96 | return 0; | ||
97 | |||
98 | in->ops.dpi->set_data_lines(in, ddata->data_lines); | ||
99 | in->ops.dpi->set_timings(in, &ddata->videomode); | ||
100 | |||
101 | r = in->ops.dpi->enable(in); | ||
102 | if (r) | ||
103 | return r; | ||
104 | |||
105 | /* wait couple of vsyncs until enabling the LCD */ | ||
106 | msleep(50); | ||
107 | |||
108 | if (gpio_is_valid(ddata->resb_gpio)) | ||
109 | gpio_set_value_cansleep(ddata->resb_gpio, 1); | ||
110 | |||
111 | if (gpio_is_valid(ddata->ini_gpio)) | ||
112 | gpio_set_value_cansleep(ddata->ini_gpio, 1); | ||
113 | |||
114 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | ||
115 | |||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | static void sharp_ls_disable(struct omap_dss_device *dssdev) | ||
120 | { | ||
121 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
122 | struct omap_dss_device *in = ddata->in; | ||
123 | |||
124 | if (!omapdss_device_is_enabled(dssdev)) | ||
125 | return; | ||
126 | |||
127 | if (gpio_is_valid(ddata->ini_gpio)) | ||
128 | gpio_set_value_cansleep(ddata->ini_gpio, 0); | ||
129 | |||
130 | if (gpio_is_valid(ddata->resb_gpio)) | ||
131 | gpio_set_value_cansleep(ddata->resb_gpio, 0); | ||
132 | |||
133 | /* wait at least 5 vsyncs after disabling the LCD */ | ||
134 | |||
135 | msleep(100); | ||
136 | |||
137 | in->ops.dpi->disable(in); | ||
138 | |||
139 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; | ||
140 | } | ||
141 | |||
142 | static void sharp_ls_set_timings(struct omap_dss_device *dssdev, | ||
143 | struct omap_video_timings *timings) | ||
144 | { | ||
145 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
146 | struct omap_dss_device *in = ddata->in; | ||
147 | |||
148 | ddata->videomode = *timings; | ||
149 | dssdev->panel.timings = *timings; | ||
150 | |||
151 | in->ops.dpi->set_timings(in, timings); | ||
152 | } | ||
153 | |||
154 | static void sharp_ls_get_timings(struct omap_dss_device *dssdev, | ||
155 | struct omap_video_timings *timings) | ||
156 | { | ||
157 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
158 | |||
159 | *timings = ddata->videomode; | ||
160 | } | ||
161 | |||
162 | static int sharp_ls_check_timings(struct omap_dss_device *dssdev, | ||
163 | struct omap_video_timings *timings) | ||
164 | { | ||
165 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
166 | struct omap_dss_device *in = ddata->in; | ||
167 | |||
168 | return in->ops.dpi->check_timings(in, timings); | ||
169 | } | ||
170 | |||
171 | static struct omap_dss_driver sharp_ls_ops = { | ||
172 | .connect = sharp_ls_connect, | ||
173 | .disconnect = sharp_ls_disconnect, | ||
174 | |||
175 | .enable = sharp_ls_enable, | ||
176 | .disable = sharp_ls_disable, | ||
177 | |||
178 | .set_timings = sharp_ls_set_timings, | ||
179 | .get_timings = sharp_ls_get_timings, | ||
180 | .check_timings = sharp_ls_check_timings, | ||
181 | |||
182 | .get_resolution = omapdss_default_get_resolution, | ||
183 | }; | ||
184 | |||
185 | static int sharp_ls_probe_pdata(struct platform_device *pdev) | ||
186 | { | ||
187 | const struct panel_sharp_ls037v7dw01_platform_data *pdata; | ||
188 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
189 | struct omap_dss_device *dssdev, *in; | ||
190 | |||
191 | pdata = dev_get_platdata(&pdev->dev); | ||
192 | |||
193 | in = omap_dss_find_output(pdata->source); | ||
194 | if (in == NULL) { | ||
195 | dev_err(&pdev->dev, "failed to find video source '%s'\n", | ||
196 | pdata->source); | ||
197 | return -EPROBE_DEFER; | ||
198 | } | ||
199 | |||
200 | ddata->in = in; | ||
201 | |||
202 | ddata->data_lines = pdata->data_lines; | ||
203 | |||
204 | dssdev = &ddata->dssdev; | ||
205 | dssdev->name = pdata->name; | ||
206 | |||
207 | ddata->resb_gpio = pdata->resb_gpio; | ||
208 | ddata->ini_gpio = pdata->ini_gpio; | ||
209 | ddata->mo_gpio = pdata->mo_gpio; | ||
210 | ddata->lr_gpio = pdata->lr_gpio; | ||
211 | ddata->ud_gpio = pdata->ud_gpio; | ||
212 | |||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | static int sharp_ls_probe(struct platform_device *pdev) | ||
217 | { | ||
218 | struct panel_drv_data *ddata; | ||
219 | struct omap_dss_device *dssdev; | ||
220 | int r; | ||
221 | |||
222 | ddata = devm_kzalloc(&pdev->dev, sizeof(*ddata), GFP_KERNEL); | ||
223 | if (ddata == NULL) | ||
224 | return -ENOMEM; | ||
225 | |||
226 | platform_set_drvdata(pdev, ddata); | ||
227 | |||
228 | if (dev_get_platdata(&pdev->dev)) { | ||
229 | r = sharp_ls_probe_pdata(pdev); | ||
230 | if (r) | ||
231 | return r; | ||
232 | } else { | ||
233 | return -ENODEV; | ||
234 | } | ||
235 | |||
236 | if (gpio_is_valid(ddata->mo_gpio)) { | ||
237 | r = devm_gpio_request_one(&pdev->dev, ddata->mo_gpio, | ||
238 | GPIOF_OUT_INIT_LOW, "lcd MO"); | ||
239 | if (r) | ||
240 | goto err_gpio; | ||
241 | } | ||
242 | |||
243 | if (gpio_is_valid(ddata->lr_gpio)) { | ||
244 | r = devm_gpio_request_one(&pdev->dev, ddata->lr_gpio, | ||
245 | GPIOF_OUT_INIT_HIGH, "lcd LR"); | ||
246 | if (r) | ||
247 | goto err_gpio; | ||
248 | } | ||
249 | |||
250 | if (gpio_is_valid(ddata->ud_gpio)) { | ||
251 | r = devm_gpio_request_one(&pdev->dev, ddata->ud_gpio, | ||
252 | GPIOF_OUT_INIT_HIGH, "lcd UD"); | ||
253 | if (r) | ||
254 | goto err_gpio; | ||
255 | } | ||
256 | |||
257 | if (gpio_is_valid(ddata->resb_gpio)) { | ||
258 | r = devm_gpio_request_one(&pdev->dev, ddata->resb_gpio, | ||
259 | GPIOF_OUT_INIT_LOW, "lcd RESB"); | ||
260 | if (r) | ||
261 | goto err_gpio; | ||
262 | } | ||
263 | |||
264 | if (gpio_is_valid(ddata->ini_gpio)) { | ||
265 | r = devm_gpio_request_one(&pdev->dev, ddata->ini_gpio, | ||
266 | GPIOF_OUT_INIT_LOW, "lcd INI"); | ||
267 | if (r) | ||
268 | goto err_gpio; | ||
269 | } | ||
270 | |||
271 | ddata->videomode = sharp_ls_timings; | ||
272 | |||
273 | dssdev = &ddata->dssdev; | ||
274 | dssdev->dev = &pdev->dev; | ||
275 | dssdev->driver = &sharp_ls_ops; | ||
276 | dssdev->type = OMAP_DISPLAY_TYPE_DPI; | ||
277 | dssdev->owner = THIS_MODULE; | ||
278 | dssdev->panel.timings = ddata->videomode; | ||
279 | dssdev->phy.dpi.data_lines = ddata->data_lines; | ||
280 | |||
281 | r = omapdss_register_display(dssdev); | ||
282 | if (r) { | ||
283 | dev_err(&pdev->dev, "Failed to register panel\n"); | ||
284 | goto err_reg; | ||
285 | } | ||
286 | |||
287 | return 0; | ||
288 | |||
289 | err_reg: | ||
290 | err_gpio: | ||
291 | omap_dss_put_device(ddata->in); | ||
292 | return r; | ||
293 | } | ||
294 | |||
295 | static int __exit sharp_ls_remove(struct platform_device *pdev) | ||
296 | { | ||
297 | struct panel_drv_data *ddata = platform_get_drvdata(pdev); | ||
298 | struct omap_dss_device *dssdev = &ddata->dssdev; | ||
299 | struct omap_dss_device *in = ddata->in; | ||
300 | |||
301 | omapdss_unregister_display(dssdev); | ||
302 | |||
303 | sharp_ls_disable(dssdev); | ||
304 | sharp_ls_disconnect(dssdev); | ||
305 | |||
306 | omap_dss_put_device(in); | ||
307 | |||
308 | return 0; | ||
309 | } | ||
310 | |||
311 | static struct platform_driver sharp_ls_driver = { | ||
312 | .probe = sharp_ls_probe, | ||
313 | .remove = __exit_p(sharp_ls_remove), | ||
314 | .driver = { | ||
315 | .name = "panel-sharp-ls037v7dw01", | ||
316 | .owner = THIS_MODULE, | ||
317 | }, | ||
318 | }; | ||
319 | |||
320 | module_platform_driver(sharp_ls_driver); | ||
321 | |||
322 | MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>"); | ||
323 | MODULE_DESCRIPTION("Sharp LS037V7DW01 Panel Driver"); | ||
324 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/omap2/displays-new/panel-sony-acx565akm.c b/drivers/video/omap2/displays-new/panel-sony-acx565akm.c new file mode 100644 index 000000000000..e6d56f714ae4 --- /dev/null +++ b/drivers/video/omap2/displays-new/panel-sony-acx565akm.c | |||
@@ -0,0 +1,865 @@ | |||
1 | /* | ||
2 | * Sony ACX565AKM LCD Panel driver | ||
3 | * | ||
4 | * Copyright (C) 2010 Nokia Corporation | ||
5 | * | ||
6 | * Original Driver Author: Imre Deak <imre.deak@nokia.com> | ||
7 | * Based on panel-generic.c by Tomi Valkeinen <tomi.valkeinen@nokia.com> | ||
8 | * Adapted to new DSS2 framework: Roger Quadros <roger.quadros@nokia.com> | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify it | ||
11 | * under the terms of the GNU General Public License version 2 as published by | ||
12 | * the Free Software Foundation. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, but WITHOUT | ||
15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | ||
16 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | ||
17 | * more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License along with | ||
20 | * this program. If not, see <http://www.gnu.org/licenses/>. | ||
21 | */ | ||
22 | |||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/platform_device.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/spi/spi.h> | ||
28 | #include <linux/jiffies.h> | ||
29 | #include <linux/sched.h> | ||
30 | #include <linux/backlight.h> | ||
31 | #include <linux/fb.h> | ||
32 | #include <linux/gpio.h> | ||
33 | |||
34 | #include <video/omapdss.h> | ||
35 | #include <video/omap-panel-data.h> | ||
36 | |||
37 | #define MIPID_CMD_READ_DISP_ID 0x04 | ||
38 | #define MIPID_CMD_READ_RED 0x06 | ||
39 | #define MIPID_CMD_READ_GREEN 0x07 | ||
40 | #define MIPID_CMD_READ_BLUE 0x08 | ||
41 | #define MIPID_CMD_READ_DISP_STATUS 0x09 | ||
42 | #define MIPID_CMD_RDDSDR 0x0F | ||
43 | #define MIPID_CMD_SLEEP_IN 0x10 | ||
44 | #define MIPID_CMD_SLEEP_OUT 0x11 | ||
45 | #define MIPID_CMD_DISP_OFF 0x28 | ||
46 | #define MIPID_CMD_DISP_ON 0x29 | ||
47 | #define MIPID_CMD_WRITE_DISP_BRIGHTNESS 0x51 | ||
48 | #define MIPID_CMD_READ_DISP_BRIGHTNESS 0x52 | ||
49 | #define MIPID_CMD_WRITE_CTRL_DISP 0x53 | ||
50 | |||
51 | #define CTRL_DISP_BRIGHTNESS_CTRL_ON (1 << 5) | ||
52 | #define CTRL_DISP_AMBIENT_LIGHT_CTRL_ON (1 << 4) | ||
53 | #define CTRL_DISP_BACKLIGHT_ON (1 << 2) | ||
54 | #define CTRL_DISP_AUTO_BRIGHTNESS_ON (1 << 1) | ||
55 | |||
56 | #define MIPID_CMD_READ_CTRL_DISP 0x54 | ||
57 | #define MIPID_CMD_WRITE_CABC 0x55 | ||
58 | #define MIPID_CMD_READ_CABC 0x56 | ||
59 | |||
60 | #define MIPID_VER_LPH8923 3 | ||
61 | #define MIPID_VER_LS041Y3 4 | ||
62 | #define MIPID_VER_L4F00311 8 | ||
63 | #define MIPID_VER_ACX565AKM 9 | ||
64 | |||
65 | struct panel_drv_data { | ||
66 | struct omap_dss_device dssdev; | ||
67 | struct omap_dss_device *in; | ||
68 | |||
69 | int reset_gpio; | ||
70 | int datapairs; | ||
71 | |||
72 | struct omap_video_timings videomode; | ||
73 | |||
74 | char *name; | ||
75 | int enabled; | ||
76 | int model; | ||
77 | int revision; | ||
78 | u8 display_id[3]; | ||
79 | unsigned has_bc:1; | ||
80 | unsigned has_cabc:1; | ||
81 | unsigned cabc_mode; | ||
82 | unsigned long hw_guard_end; /* next value of jiffies | ||
83 | when we can issue the | ||
84 | next sleep in/out command */ | ||
85 | unsigned long hw_guard_wait; /* max guard time in jiffies */ | ||
86 | |||
87 | struct spi_device *spi; | ||
88 | struct mutex mutex; | ||
89 | |||
90 | struct backlight_device *bl_dev; | ||
91 | }; | ||
92 | |||
93 | static const struct omap_video_timings acx565akm_panel_timings = { | ||
94 | .x_res = 800, | ||
95 | .y_res = 480, | ||
96 | .pixel_clock = 24000, | ||
97 | .hfp = 28, | ||
98 | .hsw = 4, | ||
99 | .hbp = 24, | ||
100 | .vfp = 3, | ||
101 | .vsw = 3, | ||
102 | .vbp = 4, | ||
103 | |||
104 | .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, | ||
105 | .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, | ||
106 | |||
107 | .data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE, | ||
108 | .de_level = OMAPDSS_SIG_ACTIVE_HIGH, | ||
109 | .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, | ||
110 | }; | ||
111 | |||
112 | #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) | ||
113 | |||
114 | static void acx565akm_transfer(struct panel_drv_data *ddata, int cmd, | ||
115 | const u8 *wbuf, int wlen, u8 *rbuf, int rlen) | ||
116 | { | ||
117 | struct spi_message m; | ||
118 | struct spi_transfer *x, xfer[5]; | ||
119 | int r; | ||
120 | |||
121 | BUG_ON(ddata->spi == NULL); | ||
122 | |||
123 | spi_message_init(&m); | ||
124 | |||
125 | memset(xfer, 0, sizeof(xfer)); | ||
126 | x = &xfer[0]; | ||
127 | |||
128 | cmd &= 0xff; | ||
129 | x->tx_buf = &cmd; | ||
130 | x->bits_per_word = 9; | ||
131 | x->len = 2; | ||
132 | |||
133 | if (rlen > 1 && wlen == 0) { | ||
134 | /* | ||
135 | * Between the command and the response data there is a | ||
136 | * dummy clock cycle. Add an extra bit after the command | ||
137 | * word to account for this. | ||
138 | */ | ||
139 | x->bits_per_word = 10; | ||
140 | cmd <<= 1; | ||
141 | } | ||
142 | spi_message_add_tail(x, &m); | ||
143 | |||
144 | if (wlen) { | ||
145 | x++; | ||
146 | x->tx_buf = wbuf; | ||
147 | x->len = wlen; | ||
148 | x->bits_per_word = 9; | ||
149 | spi_message_add_tail(x, &m); | ||
150 | } | ||
151 | |||
152 | if (rlen) { | ||
153 | x++; | ||
154 | x->rx_buf = rbuf; | ||
155 | x->len = rlen; | ||
156 | spi_message_add_tail(x, &m); | ||
157 | } | ||
158 | |||
159 | r = spi_sync(ddata->spi, &m); | ||
160 | if (r < 0) | ||
161 | dev_dbg(&ddata->spi->dev, "spi_sync %d\n", r); | ||
162 | } | ||
163 | |||
164 | static inline void acx565akm_cmd(struct panel_drv_data *ddata, int cmd) | ||
165 | { | ||
166 | acx565akm_transfer(ddata, cmd, NULL, 0, NULL, 0); | ||
167 | } | ||
168 | |||
169 | static inline void acx565akm_write(struct panel_drv_data *ddata, | ||
170 | int reg, const u8 *buf, int len) | ||
171 | { | ||
172 | acx565akm_transfer(ddata, reg, buf, len, NULL, 0); | ||
173 | } | ||
174 | |||
175 | static inline void acx565akm_read(struct panel_drv_data *ddata, | ||
176 | int reg, u8 *buf, int len) | ||
177 | { | ||
178 | acx565akm_transfer(ddata, reg, NULL, 0, buf, len); | ||
179 | } | ||
180 | |||
181 | static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec) | ||
182 | { | ||
183 | ddata->hw_guard_wait = msecs_to_jiffies(guard_msec); | ||
184 | ddata->hw_guard_end = jiffies + ddata->hw_guard_wait; | ||
185 | } | ||
186 | |||
187 | static void hw_guard_wait(struct panel_drv_data *ddata) | ||
188 | { | ||
189 | unsigned long wait = ddata->hw_guard_end - jiffies; | ||
190 | |||
191 | if ((long)wait > 0 && wait <= ddata->hw_guard_wait) { | ||
192 | set_current_state(TASK_UNINTERRUPTIBLE); | ||
193 | schedule_timeout(wait); | ||
194 | } | ||
195 | } | ||
196 | |||
197 | static void set_sleep_mode(struct panel_drv_data *ddata, int on) | ||
198 | { | ||
199 | int cmd; | ||
200 | |||
201 | if (on) | ||
202 | cmd = MIPID_CMD_SLEEP_IN; | ||
203 | else | ||
204 | cmd = MIPID_CMD_SLEEP_OUT; | ||
205 | /* | ||
206 | * We have to keep 120msec between sleep in/out commands. | ||
207 | * (8.2.15, 8.2.16). | ||
208 | */ | ||
209 | hw_guard_wait(ddata); | ||
210 | acx565akm_cmd(ddata, cmd); | ||
211 | hw_guard_start(ddata, 120); | ||
212 | } | ||
213 | |||
214 | static void set_display_state(struct panel_drv_data *ddata, int enabled) | ||
215 | { | ||
216 | int cmd = enabled ? MIPID_CMD_DISP_ON : MIPID_CMD_DISP_OFF; | ||
217 | |||
218 | acx565akm_cmd(ddata, cmd); | ||
219 | } | ||
220 | |||
221 | static int panel_enabled(struct panel_drv_data *ddata) | ||
222 | { | ||
223 | u32 disp_status; | ||
224 | int enabled; | ||
225 | |||
226 | acx565akm_read(ddata, MIPID_CMD_READ_DISP_STATUS, | ||
227 | (u8 *)&disp_status, 4); | ||
228 | disp_status = __be32_to_cpu(disp_status); | ||
229 | enabled = (disp_status & (1 << 17)) && (disp_status & (1 << 10)); | ||
230 | dev_dbg(&ddata->spi->dev, | ||
231 | "LCD panel %senabled by bootloader (status 0x%04x)\n", | ||
232 | enabled ? "" : "not ", disp_status); | ||
233 | return enabled; | ||
234 | } | ||
235 | |||
236 | static int panel_detect(struct panel_drv_data *ddata) | ||
237 | { | ||
238 | acx565akm_read(ddata, MIPID_CMD_READ_DISP_ID, ddata->display_id, 3); | ||
239 | dev_dbg(&ddata->spi->dev, "MIPI display ID: %02x%02x%02x\n", | ||
240 | ddata->display_id[0], | ||
241 | ddata->display_id[1], | ||
242 | ddata->display_id[2]); | ||
243 | |||
244 | switch (ddata->display_id[0]) { | ||
245 | case 0x10: | ||
246 | ddata->model = MIPID_VER_ACX565AKM; | ||
247 | ddata->name = "acx565akm"; | ||
248 | ddata->has_bc = 1; | ||
249 | ddata->has_cabc = 1; | ||
250 | break; | ||
251 | case 0x29: | ||
252 | ddata->model = MIPID_VER_L4F00311; | ||
253 | ddata->name = "l4f00311"; | ||
254 | break; | ||
255 | case 0x45: | ||
256 | ddata->model = MIPID_VER_LPH8923; | ||
257 | ddata->name = "lph8923"; | ||
258 | break; | ||
259 | case 0x83: | ||
260 | ddata->model = MIPID_VER_LS041Y3; | ||
261 | ddata->name = "ls041y3"; | ||
262 | break; | ||
263 | default: | ||
264 | ddata->name = "unknown"; | ||
265 | dev_err(&ddata->spi->dev, "invalid display ID\n"); | ||
266 | return -ENODEV; | ||
267 | } | ||
268 | |||
269 | ddata->revision = ddata->display_id[1]; | ||
270 | |||
271 | dev_info(&ddata->spi->dev, "omapfb: %s rev %02x LCD detected\n", | ||
272 | ddata->name, ddata->revision); | ||
273 | |||
274 | return 0; | ||
275 | } | ||
276 | |||
277 | /*----------------------Backlight Control-------------------------*/ | ||
278 | |||
279 | static void enable_backlight_ctrl(struct panel_drv_data *ddata, int enable) | ||
280 | { | ||
281 | u16 ctrl; | ||
282 | |||
283 | acx565akm_read(ddata, MIPID_CMD_READ_CTRL_DISP, (u8 *)&ctrl, 1); | ||
284 | if (enable) { | ||
285 | ctrl |= CTRL_DISP_BRIGHTNESS_CTRL_ON | | ||
286 | CTRL_DISP_BACKLIGHT_ON; | ||
287 | } else { | ||
288 | ctrl &= ~(CTRL_DISP_BRIGHTNESS_CTRL_ON | | ||
289 | CTRL_DISP_BACKLIGHT_ON); | ||
290 | } | ||
291 | |||
292 | ctrl |= 1 << 8; | ||
293 | acx565akm_write(ddata, MIPID_CMD_WRITE_CTRL_DISP, (u8 *)&ctrl, 2); | ||
294 | } | ||
295 | |||
296 | static void set_cabc_mode(struct panel_drv_data *ddata, unsigned mode) | ||
297 | { | ||
298 | u16 cabc_ctrl; | ||
299 | |||
300 | ddata->cabc_mode = mode; | ||
301 | if (!ddata->enabled) | ||
302 | return; | ||
303 | cabc_ctrl = 0; | ||
304 | acx565akm_read(ddata, MIPID_CMD_READ_CABC, (u8 *)&cabc_ctrl, 1); | ||
305 | cabc_ctrl &= ~3; | ||
306 | cabc_ctrl |= (1 << 8) | (mode & 3); | ||
307 | acx565akm_write(ddata, MIPID_CMD_WRITE_CABC, (u8 *)&cabc_ctrl, 2); | ||
308 | } | ||
309 | |||
310 | static unsigned get_cabc_mode(struct panel_drv_data *ddata) | ||
311 | { | ||
312 | return ddata->cabc_mode; | ||
313 | } | ||
314 | |||
315 | static unsigned get_hw_cabc_mode(struct panel_drv_data *ddata) | ||
316 | { | ||
317 | u8 cabc_ctrl; | ||
318 | |||
319 | acx565akm_read(ddata, MIPID_CMD_READ_CABC, &cabc_ctrl, 1); | ||
320 | return cabc_ctrl & 3; | ||
321 | } | ||
322 | |||
323 | static void acx565akm_set_brightness(struct panel_drv_data *ddata, int level) | ||
324 | { | ||
325 | int bv; | ||
326 | |||
327 | bv = level | (1 << 8); | ||
328 | acx565akm_write(ddata, MIPID_CMD_WRITE_DISP_BRIGHTNESS, (u8 *)&bv, 2); | ||
329 | |||
330 | if (level) | ||
331 | enable_backlight_ctrl(ddata, 1); | ||
332 | else | ||
333 | enable_backlight_ctrl(ddata, 0); | ||
334 | } | ||
335 | |||
336 | static int acx565akm_get_actual_brightness(struct panel_drv_data *ddata) | ||
337 | { | ||
338 | u8 bv; | ||
339 | |||
340 | acx565akm_read(ddata, MIPID_CMD_READ_DISP_BRIGHTNESS, &bv, 1); | ||
341 | |||
342 | return bv; | ||
343 | } | ||
344 | |||
345 | |||
346 | static int acx565akm_bl_update_status(struct backlight_device *dev) | ||
347 | { | ||
348 | struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev); | ||
349 | int r; | ||
350 | int level; | ||
351 | |||
352 | dev_dbg(&ddata->spi->dev, "%s\n", __func__); | ||
353 | |||
354 | mutex_lock(&ddata->mutex); | ||
355 | |||
356 | if (dev->props.fb_blank == FB_BLANK_UNBLANK && | ||
357 | dev->props.power == FB_BLANK_UNBLANK) | ||
358 | level = dev->props.brightness; | ||
359 | else | ||
360 | level = 0; | ||
361 | |||
362 | r = 0; | ||
363 | if (ddata->has_bc) | ||
364 | acx565akm_set_brightness(ddata, level); | ||
365 | else | ||
366 | r = -ENODEV; | ||
367 | |||
368 | mutex_unlock(&ddata->mutex); | ||
369 | |||
370 | return r; | ||
371 | } | ||
372 | |||
373 | static int acx565akm_bl_get_intensity(struct backlight_device *dev) | ||
374 | { | ||
375 | struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev); | ||
376 | |||
377 | dev_dbg(&dev->dev, "%s\n", __func__); | ||
378 | |||
379 | if (!ddata->has_bc) | ||
380 | return -ENODEV; | ||
381 | |||
382 | if (dev->props.fb_blank == FB_BLANK_UNBLANK && | ||
383 | dev->props.power == FB_BLANK_UNBLANK) { | ||
384 | if (ddata->has_bc) | ||
385 | return acx565akm_get_actual_brightness(ddata); | ||
386 | else | ||
387 | return dev->props.brightness; | ||
388 | } | ||
389 | |||
390 | return 0; | ||
391 | } | ||
392 | |||
393 | static const struct backlight_ops acx565akm_bl_ops = { | ||
394 | .get_brightness = acx565akm_bl_get_intensity, | ||
395 | .update_status = acx565akm_bl_update_status, | ||
396 | }; | ||
397 | |||
398 | /*--------------------Auto Brightness control via Sysfs---------------------*/ | ||
399 | |||
400 | static const char * const cabc_modes[] = { | ||
401 | "off", /* always used when CABC is not supported */ | ||
402 | "ui", | ||
403 | "still-image", | ||
404 | "moving-image", | ||
405 | }; | ||
406 | |||
407 | static ssize_t show_cabc_mode(struct device *dev, | ||
408 | struct device_attribute *attr, | ||
409 | char *buf) | ||
410 | { | ||
411 | struct panel_drv_data *ddata = dev_get_drvdata(dev); | ||
412 | const char *mode_str; | ||
413 | int mode; | ||
414 | int len; | ||
415 | |||
416 | if (!ddata->has_cabc) | ||
417 | mode = 0; | ||
418 | else | ||
419 | mode = get_cabc_mode(ddata); | ||
420 | mode_str = "unknown"; | ||
421 | if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes)) | ||
422 | mode_str = cabc_modes[mode]; | ||
423 | len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str); | ||
424 | |||
425 | return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1; | ||
426 | } | ||
427 | |||
428 | static ssize_t store_cabc_mode(struct device *dev, | ||
429 | struct device_attribute *attr, | ||
430 | const char *buf, size_t count) | ||
431 | { | ||
432 | struct panel_drv_data *ddata = dev_get_drvdata(dev); | ||
433 | int i; | ||
434 | |||
435 | for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) { | ||
436 | const char *mode_str = cabc_modes[i]; | ||
437 | int cmp_len = strlen(mode_str); | ||
438 | |||
439 | if (count > 0 && buf[count - 1] == '\n') | ||
440 | count--; | ||
441 | if (count != cmp_len) | ||
442 | continue; | ||
443 | |||
444 | if (strncmp(buf, mode_str, cmp_len) == 0) | ||
445 | break; | ||
446 | } | ||
447 | |||
448 | if (i == ARRAY_SIZE(cabc_modes)) | ||
449 | return -EINVAL; | ||
450 | |||
451 | if (!ddata->has_cabc && i != 0) | ||
452 | return -EINVAL; | ||
453 | |||
454 | mutex_lock(&ddata->mutex); | ||
455 | set_cabc_mode(ddata, i); | ||
456 | mutex_unlock(&ddata->mutex); | ||
457 | |||
458 | return count; | ||
459 | } | ||
460 | |||
461 | static ssize_t show_cabc_available_modes(struct device *dev, | ||
462 | struct device_attribute *attr, | ||
463 | char *buf) | ||
464 | { | ||
465 | struct panel_drv_data *ddata = dev_get_drvdata(dev); | ||
466 | int len; | ||
467 | int i; | ||
468 | |||
469 | if (!ddata->has_cabc) | ||
470 | return snprintf(buf, PAGE_SIZE, "%s\n", cabc_modes[0]); | ||
471 | |||
472 | for (i = 0, len = 0; | ||
473 | len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++) | ||
474 | len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s", | ||
475 | i ? " " : "", cabc_modes[i], | ||
476 | i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : ""); | ||
477 | |||
478 | return len < PAGE_SIZE ? len : PAGE_SIZE - 1; | ||
479 | } | ||
480 | |||
481 | static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR, | ||
482 | show_cabc_mode, store_cabc_mode); | ||
483 | static DEVICE_ATTR(cabc_available_modes, S_IRUGO, | ||
484 | show_cabc_available_modes, NULL); | ||
485 | |||
486 | static struct attribute *bldev_attrs[] = { | ||
487 | &dev_attr_cabc_mode.attr, | ||
488 | &dev_attr_cabc_available_modes.attr, | ||
489 | NULL, | ||
490 | }; | ||
491 | |||
492 | static struct attribute_group bldev_attr_group = { | ||
493 | .attrs = bldev_attrs, | ||
494 | }; | ||
495 | |||
496 | static int acx565akm_connect(struct omap_dss_device *dssdev) | ||
497 | { | ||
498 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
499 | struct omap_dss_device *in = ddata->in; | ||
500 | int r; | ||
501 | |||
502 | if (omapdss_device_is_connected(dssdev)) | ||
503 | return 0; | ||
504 | |||
505 | r = in->ops.sdi->connect(in, dssdev); | ||
506 | if (r) | ||
507 | return r; | ||
508 | |||
509 | return 0; | ||
510 | } | ||
511 | |||
512 | static void acx565akm_disconnect(struct omap_dss_device *dssdev) | ||
513 | { | ||
514 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
515 | struct omap_dss_device *in = ddata->in; | ||
516 | |||
517 | if (!omapdss_device_is_connected(dssdev)) | ||
518 | return; | ||
519 | |||
520 | in->ops.sdi->disconnect(in, dssdev); | ||
521 | } | ||
522 | |||
523 | static int acx565akm_panel_power_on(struct omap_dss_device *dssdev) | ||
524 | { | ||
525 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
526 | struct omap_dss_device *in = ddata->in; | ||
527 | int r; | ||
528 | |||
529 | dev_dbg(&ddata->spi->dev, "%s\n", __func__); | ||
530 | |||
531 | in->ops.sdi->set_timings(in, &ddata->videomode); | ||
532 | in->ops.sdi->set_datapairs(in, ddata->datapairs); | ||
533 | |||
534 | r = in->ops.sdi->enable(in); | ||
535 | if (r) { | ||
536 | pr_err("%s sdi enable failed\n", __func__); | ||
537 | return r; | ||
538 | } | ||
539 | |||
540 | /*FIXME tweak me */ | ||
541 | msleep(50); | ||
542 | |||
543 | if (gpio_is_valid(ddata->reset_gpio)) | ||
544 | gpio_set_value(ddata->reset_gpio, 1); | ||
545 | |||
546 | if (ddata->enabled) { | ||
547 | dev_dbg(&ddata->spi->dev, "panel already enabled\n"); | ||
548 | return 0; | ||
549 | } | ||
550 | |||
551 | /* | ||
552 | * We have to meet all the following delay requirements: | ||
553 | * 1. tRW: reset pulse width 10usec (7.12.1) | ||
554 | * 2. tRT: reset cancel time 5msec (7.12.1) | ||
555 | * 3. Providing PCLK,HS,VS signals for 2 frames = ~50msec worst | ||
556 | * case (7.6.2) | ||
557 | * 4. 120msec before the sleep out command (7.12.1) | ||
558 | */ | ||
559 | msleep(120); | ||
560 | |||
561 | set_sleep_mode(ddata, 0); | ||
562 | ddata->enabled = 1; | ||
563 | |||
564 | /* 5msec between sleep out and the next command. (8.2.16) */ | ||
565 | usleep_range(5000, 10000); | ||
566 | set_display_state(ddata, 1); | ||
567 | set_cabc_mode(ddata, ddata->cabc_mode); | ||
568 | |||
569 | mutex_unlock(&ddata->mutex); | ||
570 | |||
571 | return acx565akm_bl_update_status(ddata->bl_dev); | ||
572 | } | ||
573 | |||
574 | static void acx565akm_panel_power_off(struct omap_dss_device *dssdev) | ||
575 | { | ||
576 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
577 | struct omap_dss_device *in = ddata->in; | ||
578 | |||
579 | dev_dbg(dssdev->dev, "%s\n", __func__); | ||
580 | |||
581 | if (!ddata->enabled) | ||
582 | return; | ||
583 | |||
584 | set_display_state(ddata, 0); | ||
585 | set_sleep_mode(ddata, 1); | ||
586 | ddata->enabled = 0; | ||
587 | /* | ||
588 | * We have to provide PCLK,HS,VS signals for 2 frames (worst case | ||
589 | * ~50msec) after sending the sleep in command and asserting the | ||
590 | * reset signal. We probably could assert the reset w/o the delay | ||
591 | * but we still delay to avoid possible artifacts. (7.6.1) | ||
592 | */ | ||
593 | msleep(50); | ||
594 | |||
595 | if (gpio_is_valid(ddata->reset_gpio)) | ||
596 | gpio_set_value(ddata->reset_gpio, 0); | ||
597 | |||
598 | /* FIXME need to tweak this delay */ | ||
599 | msleep(100); | ||
600 | |||
601 | in->ops.sdi->disable(in); | ||
602 | } | ||
603 | |||
604 | static int acx565akm_enable(struct omap_dss_device *dssdev) | ||
605 | { | ||
606 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
607 | int r; | ||
608 | |||
609 | dev_dbg(dssdev->dev, "%s\n", __func__); | ||
610 | |||
611 | if (!omapdss_device_is_connected(dssdev)) | ||
612 | return -ENODEV; | ||
613 | |||
614 | if (omapdss_device_is_enabled(dssdev)) | ||
615 | return 0; | ||
616 | |||
617 | mutex_lock(&ddata->mutex); | ||
618 | r = acx565akm_panel_power_on(dssdev); | ||
619 | mutex_unlock(&ddata->mutex); | ||
620 | |||
621 | if (r) | ||
622 | return r; | ||
623 | |||
624 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | ||
625 | |||
626 | return 0; | ||
627 | } | ||
628 | |||
629 | static void acx565akm_disable(struct omap_dss_device *dssdev) | ||
630 | { | ||
631 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
632 | |||
633 | dev_dbg(dssdev->dev, "%s\n", __func__); | ||
634 | |||
635 | if (!omapdss_device_is_enabled(dssdev)) | ||
636 | return; | ||
637 | |||
638 | mutex_lock(&ddata->mutex); | ||
639 | acx565akm_panel_power_off(dssdev); | ||
640 | mutex_unlock(&ddata->mutex); | ||
641 | |||
642 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; | ||
643 | } | ||
644 | |||
645 | static void acx565akm_set_timings(struct omap_dss_device *dssdev, | ||
646 | struct omap_video_timings *timings) | ||
647 | { | ||
648 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
649 | struct omap_dss_device *in = ddata->in; | ||
650 | |||
651 | ddata->videomode = *timings; | ||
652 | dssdev->panel.timings = *timings; | ||
653 | |||
654 | in->ops.sdi->set_timings(in, timings); | ||
655 | } | ||
656 | |||
657 | static void acx565akm_get_timings(struct omap_dss_device *dssdev, | ||
658 | struct omap_video_timings *timings) | ||
659 | { | ||
660 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
661 | |||
662 | *timings = ddata->videomode; | ||
663 | } | ||
664 | |||
665 | static int acx565akm_check_timings(struct omap_dss_device *dssdev, | ||
666 | struct omap_video_timings *timings) | ||
667 | { | ||
668 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
669 | struct omap_dss_device *in = ddata->in; | ||
670 | |||
671 | return in->ops.sdi->check_timings(in, timings); | ||
672 | } | ||
673 | |||
674 | static struct omap_dss_driver acx565akm_ops = { | ||
675 | .connect = acx565akm_connect, | ||
676 | .disconnect = acx565akm_disconnect, | ||
677 | |||
678 | .enable = acx565akm_enable, | ||
679 | .disable = acx565akm_disable, | ||
680 | |||
681 | .set_timings = acx565akm_set_timings, | ||
682 | .get_timings = acx565akm_get_timings, | ||
683 | .check_timings = acx565akm_check_timings, | ||
684 | |||
685 | .get_resolution = omapdss_default_get_resolution, | ||
686 | }; | ||
687 | |||
688 | static int acx565akm_probe_pdata(struct spi_device *spi) | ||
689 | { | ||
690 | const struct panel_acx565akm_platform_data *pdata; | ||
691 | struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); | ||
692 | struct omap_dss_device *dssdev, *in; | ||
693 | |||
694 | pdata = dev_get_platdata(&spi->dev); | ||
695 | |||
696 | ddata->reset_gpio = pdata->reset_gpio; | ||
697 | |||
698 | in = omap_dss_find_output(pdata->source); | ||
699 | if (in == NULL) { | ||
700 | dev_err(&spi->dev, "failed to find video source '%s'\n", | ||
701 | pdata->source); | ||
702 | return -EPROBE_DEFER; | ||
703 | } | ||
704 | ddata->in = in; | ||
705 | |||
706 | ddata->datapairs = pdata->datapairs; | ||
707 | |||
708 | dssdev = &ddata->dssdev; | ||
709 | dssdev->name = pdata->name; | ||
710 | |||
711 | return 0; | ||
712 | } | ||
713 | |||
714 | static int acx565akm_probe(struct spi_device *spi) | ||
715 | { | ||
716 | struct panel_drv_data *ddata; | ||
717 | struct omap_dss_device *dssdev; | ||
718 | struct backlight_device *bldev; | ||
719 | int max_brightness, brightness; | ||
720 | struct backlight_properties props; | ||
721 | int r; | ||
722 | |||
723 | dev_dbg(&spi->dev, "%s\n", __func__); | ||
724 | |||
725 | spi->mode = SPI_MODE_3; | ||
726 | |||
727 | ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL); | ||
728 | if (ddata == NULL) | ||
729 | return -ENOMEM; | ||
730 | |||
731 | dev_set_drvdata(&spi->dev, ddata); | ||
732 | |||
733 | ddata->spi = spi; | ||
734 | |||
735 | mutex_init(&ddata->mutex); | ||
736 | |||
737 | if (dev_get_platdata(&spi->dev)) { | ||
738 | r = acx565akm_probe_pdata(spi); | ||
739 | if (r) | ||
740 | return r; | ||
741 | } else { | ||
742 | return -ENODEV; | ||
743 | } | ||
744 | |||
745 | if (gpio_is_valid(ddata->reset_gpio)) { | ||
746 | r = devm_gpio_request_one(&spi->dev, ddata->reset_gpio, | ||
747 | GPIOF_OUT_INIT_LOW, "lcd reset"); | ||
748 | if (r) | ||
749 | goto err_gpio; | ||
750 | } | ||
751 | |||
752 | if (gpio_is_valid(ddata->reset_gpio)) | ||
753 | gpio_set_value(ddata->reset_gpio, 1); | ||
754 | |||
755 | /* | ||
756 | * After reset we have to wait 5 msec before the first | ||
757 | * command can be sent. | ||
758 | */ | ||
759 | usleep_range(5000, 10000); | ||
760 | |||
761 | ddata->enabled = panel_enabled(ddata); | ||
762 | |||
763 | r = panel_detect(ddata); | ||
764 | |||
765 | if (!ddata->enabled && gpio_is_valid(ddata->reset_gpio)) | ||
766 | gpio_set_value(ddata->reset_gpio, 0); | ||
767 | |||
768 | if (r) { | ||
769 | dev_err(&spi->dev, "%s panel detect error\n", __func__); | ||
770 | goto err_detect; | ||
771 | } | ||
772 | |||
773 | memset(&props, 0, sizeof(props)); | ||
774 | props.fb_blank = FB_BLANK_UNBLANK; | ||
775 | props.power = FB_BLANK_UNBLANK; | ||
776 | props.type = BACKLIGHT_RAW; | ||
777 | |||
778 | bldev = backlight_device_register("acx565akm", &ddata->spi->dev, | ||
779 | ddata, &acx565akm_bl_ops, &props); | ||
780 | ddata->bl_dev = bldev; | ||
781 | if (ddata->has_cabc) { | ||
782 | r = sysfs_create_group(&bldev->dev.kobj, &bldev_attr_group); | ||
783 | if (r) { | ||
784 | dev_err(&bldev->dev, | ||
785 | "%s failed to create sysfs files\n", __func__); | ||
786 | goto err_sysfs; | ||
787 | } | ||
788 | ddata->cabc_mode = get_hw_cabc_mode(ddata); | ||
789 | } | ||
790 | |||
791 | max_brightness = 255; | ||
792 | |||
793 | if (ddata->has_bc) | ||
794 | brightness = acx565akm_get_actual_brightness(ddata); | ||
795 | else | ||
796 | brightness = 0; | ||
797 | |||
798 | bldev->props.max_brightness = max_brightness; | ||
799 | bldev->props.brightness = brightness; | ||
800 | |||
801 | acx565akm_bl_update_status(bldev); | ||
802 | |||
803 | |||
804 | ddata->videomode = acx565akm_panel_timings; | ||
805 | |||
806 | dssdev = &ddata->dssdev; | ||
807 | dssdev->dev = &spi->dev; | ||
808 | dssdev->driver = &acx565akm_ops; | ||
809 | dssdev->type = OMAP_DISPLAY_TYPE_SDI; | ||
810 | dssdev->owner = THIS_MODULE; | ||
811 | dssdev->panel.timings = ddata->videomode; | ||
812 | |||
813 | r = omapdss_register_display(dssdev); | ||
814 | if (r) { | ||
815 | dev_err(&spi->dev, "Failed to register panel\n"); | ||
816 | goto err_reg; | ||
817 | } | ||
818 | |||
819 | return 0; | ||
820 | |||
821 | err_reg: | ||
822 | sysfs_remove_group(&bldev->dev.kobj, &bldev_attr_group); | ||
823 | err_sysfs: | ||
824 | backlight_device_unregister(bldev); | ||
825 | err_detect: | ||
826 | err_gpio: | ||
827 | omap_dss_put_device(ddata->in); | ||
828 | return r; | ||
829 | } | ||
830 | |||
831 | static int acx565akm_remove(struct spi_device *spi) | ||
832 | { | ||
833 | struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); | ||
834 | struct omap_dss_device *dssdev = &ddata->dssdev; | ||
835 | struct omap_dss_device *in = ddata->in; | ||
836 | |||
837 | dev_dbg(&ddata->spi->dev, "%s\n", __func__); | ||
838 | |||
839 | sysfs_remove_group(&ddata->bl_dev->dev.kobj, &bldev_attr_group); | ||
840 | backlight_device_unregister(ddata->bl_dev); | ||
841 | |||
842 | omapdss_unregister_display(dssdev); | ||
843 | |||
844 | acx565akm_disable(dssdev); | ||
845 | acx565akm_disconnect(dssdev); | ||
846 | |||
847 | omap_dss_put_device(in); | ||
848 | |||
849 | return 0; | ||
850 | } | ||
851 | |||
852 | static struct spi_driver acx565akm_driver = { | ||
853 | .driver = { | ||
854 | .name = "acx565akm", | ||
855 | .owner = THIS_MODULE, | ||
856 | }, | ||
857 | .probe = acx565akm_probe, | ||
858 | .remove = acx565akm_remove, | ||
859 | }; | ||
860 | |||
861 | module_spi_driver(acx565akm_driver); | ||
862 | |||
863 | MODULE_AUTHOR("Nokia Corporation"); | ||
864 | MODULE_DESCRIPTION("acx565akm LCD Driver"); | ||
865 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/omap2/displays-new/panel-tpo-td043mtea1.c b/drivers/video/omap2/displays-new/panel-tpo-td043mtea1.c new file mode 100644 index 000000000000..eadc6529fa3d --- /dev/null +++ b/drivers/video/omap2/displays-new/panel-tpo-td043mtea1.c | |||
@@ -0,0 +1,646 @@ | |||
1 | /* | ||
2 | * TPO TD043MTEA1 Panel driver | ||
3 | * | ||
4 | * Author: Gražvydas Ignotas <notasas@gmail.com> | ||
5 | * Converted to new DSS device model: Tomi Valkeinen <tomi.valkeinen@ti.com> | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/delay.h> | ||
15 | #include <linux/spi/spi.h> | ||
16 | #include <linux/regulator/consumer.h> | ||
17 | #include <linux/gpio.h> | ||
18 | #include <linux/err.h> | ||
19 | #include <linux/slab.h> | ||
20 | |||
21 | #include <video/omapdss.h> | ||
22 | #include <video/omap-panel-data.h> | ||
23 | |||
24 | #define TPO_R02_MODE(x) ((x) & 7) | ||
25 | #define TPO_R02_MODE_800x480 7 | ||
26 | #define TPO_R02_NCLK_RISING BIT(3) | ||
27 | #define TPO_R02_HSYNC_HIGH BIT(4) | ||
28 | #define TPO_R02_VSYNC_HIGH BIT(5) | ||
29 | |||
30 | #define TPO_R03_NSTANDBY BIT(0) | ||
31 | #define TPO_R03_EN_CP_CLK BIT(1) | ||
32 | #define TPO_R03_EN_VGL_PUMP BIT(2) | ||
33 | #define TPO_R03_EN_PWM BIT(3) | ||
34 | #define TPO_R03_DRIVING_CAP_100 BIT(4) | ||
35 | #define TPO_R03_EN_PRE_CHARGE BIT(6) | ||
36 | #define TPO_R03_SOFTWARE_CTL BIT(7) | ||
37 | |||
38 | #define TPO_R04_NFLIP_H BIT(0) | ||
39 | #define TPO_R04_NFLIP_V BIT(1) | ||
40 | #define TPO_R04_CP_CLK_FREQ_1H BIT(2) | ||
41 | #define TPO_R04_VGL_FREQ_1H BIT(4) | ||
42 | |||
43 | #define TPO_R03_VAL_NORMAL (TPO_R03_NSTANDBY | TPO_R03_EN_CP_CLK | \ | ||
44 | TPO_R03_EN_VGL_PUMP | TPO_R03_EN_PWM | \ | ||
45 | TPO_R03_DRIVING_CAP_100 | TPO_R03_EN_PRE_CHARGE | \ | ||
46 | TPO_R03_SOFTWARE_CTL) | ||
47 | |||
48 | #define TPO_R03_VAL_STANDBY (TPO_R03_DRIVING_CAP_100 | \ | ||
49 | TPO_R03_EN_PRE_CHARGE | TPO_R03_SOFTWARE_CTL) | ||
50 | |||
51 | static const u16 tpo_td043_def_gamma[12] = { | ||
52 | 105, 315, 381, 431, 490, 537, 579, 686, 780, 837, 880, 1023 | ||
53 | }; | ||
54 | |||
55 | struct panel_drv_data { | ||
56 | struct omap_dss_device dssdev; | ||
57 | struct omap_dss_device *in; | ||
58 | |||
59 | struct omap_video_timings videomode; | ||
60 | |||
61 | int data_lines; | ||
62 | |||
63 | struct spi_device *spi; | ||
64 | struct regulator *vcc_reg; | ||
65 | int nreset_gpio; | ||
66 | u16 gamma[12]; | ||
67 | u32 mode; | ||
68 | u32 hmirror:1; | ||
69 | u32 vmirror:1; | ||
70 | u32 powered_on:1; | ||
71 | u32 spi_suspended:1; | ||
72 | u32 power_on_resume:1; | ||
73 | }; | ||
74 | |||
75 | static const struct omap_video_timings tpo_td043_timings = { | ||
76 | .x_res = 800, | ||
77 | .y_res = 480, | ||
78 | |||
79 | .pixel_clock = 36000, | ||
80 | |||
81 | .hsw = 1, | ||
82 | .hfp = 68, | ||
83 | .hbp = 214, | ||
84 | |||
85 | .vsw = 1, | ||
86 | .vfp = 39, | ||
87 | .vbp = 34, | ||
88 | |||
89 | .vsync_level = OMAPDSS_SIG_ACTIVE_LOW, | ||
90 | .hsync_level = OMAPDSS_SIG_ACTIVE_LOW, | ||
91 | .data_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE, | ||
92 | .de_level = OMAPDSS_SIG_ACTIVE_HIGH, | ||
93 | .sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES, | ||
94 | }; | ||
95 | |||
96 | #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev) | ||
97 | |||
98 | static int tpo_td043_write(struct spi_device *spi, u8 addr, u8 data) | ||
99 | { | ||
100 | struct spi_message m; | ||
101 | struct spi_transfer xfer; | ||
102 | u16 w; | ||
103 | int r; | ||
104 | |||
105 | spi_message_init(&m); | ||
106 | |||
107 | memset(&xfer, 0, sizeof(xfer)); | ||
108 | |||
109 | w = ((u16)addr << 10) | (1 << 8) | data; | ||
110 | xfer.tx_buf = &w; | ||
111 | xfer.bits_per_word = 16; | ||
112 | xfer.len = 2; | ||
113 | spi_message_add_tail(&xfer, &m); | ||
114 | |||
115 | r = spi_sync(spi, &m); | ||
116 | if (r < 0) | ||
117 | dev_warn(&spi->dev, "failed to write to LCD reg (%d)\n", r); | ||
118 | return r; | ||
119 | } | ||
120 | |||
121 | static void tpo_td043_write_gamma(struct spi_device *spi, u16 gamma[12]) | ||
122 | { | ||
123 | u8 i, val; | ||
124 | |||
125 | /* gamma bits [9:8] */ | ||
126 | for (val = i = 0; i < 4; i++) | ||
127 | val |= (gamma[i] & 0x300) >> ((i + 1) * 2); | ||
128 | tpo_td043_write(spi, 0x11, val); | ||
129 | |||
130 | for (val = i = 0; i < 4; i++) | ||
131 | val |= (gamma[i+4] & 0x300) >> ((i + 1) * 2); | ||
132 | tpo_td043_write(spi, 0x12, val); | ||
133 | |||
134 | for (val = i = 0; i < 4; i++) | ||
135 | val |= (gamma[i+8] & 0x300) >> ((i + 1) * 2); | ||
136 | tpo_td043_write(spi, 0x13, val); | ||
137 | |||
138 | /* gamma bits [7:0] */ | ||
139 | for (val = i = 0; i < 12; i++) | ||
140 | tpo_td043_write(spi, 0x14 + i, gamma[i] & 0xff); | ||
141 | } | ||
142 | |||
143 | static int tpo_td043_write_mirror(struct spi_device *spi, bool h, bool v) | ||
144 | { | ||
145 | u8 reg4 = TPO_R04_NFLIP_H | TPO_R04_NFLIP_V | | ||
146 | TPO_R04_CP_CLK_FREQ_1H | TPO_R04_VGL_FREQ_1H; | ||
147 | if (h) | ||
148 | reg4 &= ~TPO_R04_NFLIP_H; | ||
149 | if (v) | ||
150 | reg4 &= ~TPO_R04_NFLIP_V; | ||
151 | |||
152 | return tpo_td043_write(spi, 4, reg4); | ||
153 | } | ||
154 | |||
155 | static int tpo_td043_set_hmirror(struct omap_dss_device *dssdev, bool enable) | ||
156 | { | ||
157 | struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev); | ||
158 | |||
159 | ddata->hmirror = enable; | ||
160 | return tpo_td043_write_mirror(ddata->spi, ddata->hmirror, | ||
161 | ddata->vmirror); | ||
162 | } | ||
163 | |||
164 | static bool tpo_td043_get_hmirror(struct omap_dss_device *dssdev) | ||
165 | { | ||
166 | struct panel_drv_data *ddata = dev_get_drvdata(dssdev->dev); | ||
167 | |||
168 | return ddata->hmirror; | ||
169 | } | ||
170 | |||
171 | static ssize_t tpo_td043_vmirror_show(struct device *dev, | ||
172 | struct device_attribute *attr, char *buf) | ||
173 | { | ||
174 | struct panel_drv_data *ddata = dev_get_drvdata(dev); | ||
175 | |||
176 | return snprintf(buf, PAGE_SIZE, "%d\n", ddata->vmirror); | ||
177 | } | ||
178 | |||
179 | static ssize_t tpo_td043_vmirror_store(struct device *dev, | ||
180 | struct device_attribute *attr, const char *buf, size_t count) | ||
181 | { | ||
182 | struct panel_drv_data *ddata = dev_get_drvdata(dev); | ||
183 | int val; | ||
184 | int ret; | ||
185 | |||
186 | ret = kstrtoint(buf, 0, &val); | ||
187 | if (ret < 0) | ||
188 | return ret; | ||
189 | |||
190 | val = !!val; | ||
191 | |||
192 | ret = tpo_td043_write_mirror(ddata->spi, ddata->hmirror, val); | ||
193 | if (ret < 0) | ||
194 | return ret; | ||
195 | |||
196 | ddata->vmirror = val; | ||
197 | |||
198 | return count; | ||
199 | } | ||
200 | |||
201 | static ssize_t tpo_td043_mode_show(struct device *dev, | ||
202 | struct device_attribute *attr, char *buf) | ||
203 | { | ||
204 | struct panel_drv_data *ddata = dev_get_drvdata(dev); | ||
205 | |||
206 | return snprintf(buf, PAGE_SIZE, "%d\n", ddata->mode); | ||
207 | } | ||
208 | |||
209 | static ssize_t tpo_td043_mode_store(struct device *dev, | ||
210 | struct device_attribute *attr, const char *buf, size_t count) | ||
211 | { | ||
212 | struct panel_drv_data *ddata = dev_get_drvdata(dev); | ||
213 | long val; | ||
214 | int ret; | ||
215 | |||
216 | ret = kstrtol(buf, 0, &val); | ||
217 | if (ret != 0 || val & ~7) | ||
218 | return -EINVAL; | ||
219 | |||
220 | ddata->mode = val; | ||
221 | |||
222 | val |= TPO_R02_NCLK_RISING; | ||
223 | tpo_td043_write(ddata->spi, 2, val); | ||
224 | |||
225 | return count; | ||
226 | } | ||
227 | |||
228 | static ssize_t tpo_td043_gamma_show(struct device *dev, | ||
229 | struct device_attribute *attr, char *buf) | ||
230 | { | ||
231 | struct panel_drv_data *ddata = dev_get_drvdata(dev); | ||
232 | ssize_t len = 0; | ||
233 | int ret; | ||
234 | int i; | ||
235 | |||
236 | for (i = 0; i < ARRAY_SIZE(ddata->gamma); i++) { | ||
237 | ret = snprintf(buf + len, PAGE_SIZE - len, "%u ", | ||
238 | ddata->gamma[i]); | ||
239 | if (ret < 0) | ||
240 | return ret; | ||
241 | len += ret; | ||
242 | } | ||
243 | buf[len - 1] = '\n'; | ||
244 | |||
245 | return len; | ||
246 | } | ||
247 | |||
248 | static ssize_t tpo_td043_gamma_store(struct device *dev, | ||
249 | struct device_attribute *attr, const char *buf, size_t count) | ||
250 | { | ||
251 | struct panel_drv_data *ddata = dev_get_drvdata(dev); | ||
252 | unsigned int g[12]; | ||
253 | int ret; | ||
254 | int i; | ||
255 | |||
256 | ret = sscanf(buf, "%u %u %u %u %u %u %u %u %u %u %u %u", | ||
257 | &g[0], &g[1], &g[2], &g[3], &g[4], &g[5], | ||
258 | &g[6], &g[7], &g[8], &g[9], &g[10], &g[11]); | ||
259 | |||
260 | if (ret != 12) | ||
261 | return -EINVAL; | ||
262 | |||
263 | for (i = 0; i < 12; i++) | ||
264 | ddata->gamma[i] = g[i]; | ||
265 | |||
266 | tpo_td043_write_gamma(ddata->spi, ddata->gamma); | ||
267 | |||
268 | return count; | ||
269 | } | ||
270 | |||
271 | static DEVICE_ATTR(vmirror, S_IRUGO | S_IWUSR, | ||
272 | tpo_td043_vmirror_show, tpo_td043_vmirror_store); | ||
273 | static DEVICE_ATTR(mode, S_IRUGO | S_IWUSR, | ||
274 | tpo_td043_mode_show, tpo_td043_mode_store); | ||
275 | static DEVICE_ATTR(gamma, S_IRUGO | S_IWUSR, | ||
276 | tpo_td043_gamma_show, tpo_td043_gamma_store); | ||
277 | |||
278 | static struct attribute *tpo_td043_attrs[] = { | ||
279 | &dev_attr_vmirror.attr, | ||
280 | &dev_attr_mode.attr, | ||
281 | &dev_attr_gamma.attr, | ||
282 | NULL, | ||
283 | }; | ||
284 | |||
285 | static struct attribute_group tpo_td043_attr_group = { | ||
286 | .attrs = tpo_td043_attrs, | ||
287 | }; | ||
288 | |||
289 | static int tpo_td043_power_on(struct panel_drv_data *ddata) | ||
290 | { | ||
291 | int r; | ||
292 | |||
293 | if (ddata->powered_on) | ||
294 | return 0; | ||
295 | |||
296 | r = regulator_enable(ddata->vcc_reg); | ||
297 | if (r != 0) | ||
298 | return r; | ||
299 | |||
300 | /* wait for panel to stabilize */ | ||
301 | msleep(160); | ||
302 | |||
303 | if (gpio_is_valid(ddata->nreset_gpio)) | ||
304 | gpio_set_value(ddata->nreset_gpio, 1); | ||
305 | |||
306 | tpo_td043_write(ddata->spi, 2, | ||
307 | TPO_R02_MODE(ddata->mode) | TPO_R02_NCLK_RISING); | ||
308 | tpo_td043_write(ddata->spi, 3, TPO_R03_VAL_NORMAL); | ||
309 | tpo_td043_write(ddata->spi, 0x20, 0xf0); | ||
310 | tpo_td043_write(ddata->spi, 0x21, 0xf0); | ||
311 | tpo_td043_write_mirror(ddata->spi, ddata->hmirror, | ||
312 | ddata->vmirror); | ||
313 | tpo_td043_write_gamma(ddata->spi, ddata->gamma); | ||
314 | |||
315 | ddata->powered_on = 1; | ||
316 | return 0; | ||
317 | } | ||
318 | |||
319 | static void tpo_td043_power_off(struct panel_drv_data *ddata) | ||
320 | { | ||
321 | if (!ddata->powered_on) | ||
322 | return; | ||
323 | |||
324 | tpo_td043_write(ddata->spi, 3, | ||
325 | TPO_R03_VAL_STANDBY | TPO_R03_EN_PWM); | ||
326 | |||
327 | if (gpio_is_valid(ddata->nreset_gpio)) | ||
328 | gpio_set_value(ddata->nreset_gpio, 0); | ||
329 | |||
330 | /* wait for at least 2 vsyncs before cutting off power */ | ||
331 | msleep(50); | ||
332 | |||
333 | tpo_td043_write(ddata->spi, 3, TPO_R03_VAL_STANDBY); | ||
334 | |||
335 | regulator_disable(ddata->vcc_reg); | ||
336 | |||
337 | ddata->powered_on = 0; | ||
338 | } | ||
339 | |||
340 | static int tpo_td043_connect(struct omap_dss_device *dssdev) | ||
341 | { | ||
342 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
343 | struct omap_dss_device *in = ddata->in; | ||
344 | int r; | ||
345 | |||
346 | if (omapdss_device_is_connected(dssdev)) | ||
347 | return 0; | ||
348 | |||
349 | r = in->ops.dpi->connect(in, dssdev); | ||
350 | if (r) | ||
351 | return r; | ||
352 | |||
353 | return 0; | ||
354 | } | ||
355 | |||
356 | static void tpo_td043_disconnect(struct omap_dss_device *dssdev) | ||
357 | { | ||
358 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
359 | struct omap_dss_device *in = ddata->in; | ||
360 | |||
361 | if (!omapdss_device_is_connected(dssdev)) | ||
362 | return; | ||
363 | |||
364 | in->ops.dpi->disconnect(in, dssdev); | ||
365 | } | ||
366 | |||
367 | static int tpo_td043_enable(struct omap_dss_device *dssdev) | ||
368 | { | ||
369 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
370 | struct omap_dss_device *in = ddata->in; | ||
371 | int r; | ||
372 | |||
373 | if (!omapdss_device_is_connected(dssdev)) | ||
374 | return -ENODEV; | ||
375 | |||
376 | if (omapdss_device_is_enabled(dssdev)) | ||
377 | return 0; | ||
378 | |||
379 | in->ops.dpi->set_data_lines(in, ddata->data_lines); | ||
380 | in->ops.dpi->set_timings(in, &ddata->videomode); | ||
381 | |||
382 | r = in->ops.dpi->enable(in); | ||
383 | if (r) | ||
384 | return r; | ||
385 | |||
386 | /* | ||
387 | * If we are resuming from system suspend, SPI clocks might not be | ||
388 | * enabled yet, so we'll program the LCD from SPI PM resume callback. | ||
389 | */ | ||
390 | if (!ddata->spi_suspended) { | ||
391 | r = tpo_td043_power_on(ddata); | ||
392 | if (r) { | ||
393 | in->ops.dpi->disable(in); | ||
394 | return r; | ||
395 | } | ||
396 | } | ||
397 | |||
398 | dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; | ||
399 | |||
400 | return 0; | ||
401 | } | ||
402 | |||
403 | static void tpo_td043_disable(struct omap_dss_device *dssdev) | ||
404 | { | ||
405 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
406 | struct omap_dss_device *in = ddata->in; | ||
407 | |||
408 | if (!omapdss_device_is_enabled(dssdev)) | ||
409 | return; | ||
410 | |||
411 | in->ops.dpi->disable(in); | ||
412 | |||
413 | if (!ddata->spi_suspended) | ||
414 | tpo_td043_power_off(ddata); | ||
415 | |||
416 | dssdev->state = OMAP_DSS_DISPLAY_DISABLED; | ||
417 | } | ||
418 | |||
419 | static void tpo_td043_set_timings(struct omap_dss_device *dssdev, | ||
420 | struct omap_video_timings *timings) | ||
421 | { | ||
422 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
423 | struct omap_dss_device *in = ddata->in; | ||
424 | |||
425 | ddata->videomode = *timings; | ||
426 | dssdev->panel.timings = *timings; | ||
427 | |||
428 | in->ops.dpi->set_timings(in, timings); | ||
429 | } | ||
430 | |||
431 | static void tpo_td043_get_timings(struct omap_dss_device *dssdev, | ||
432 | struct omap_video_timings *timings) | ||
433 | { | ||
434 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
435 | |||
436 | *timings = ddata->videomode; | ||
437 | } | ||
438 | |||
439 | static int tpo_td043_check_timings(struct omap_dss_device *dssdev, | ||
440 | struct omap_video_timings *timings) | ||
441 | { | ||
442 | struct panel_drv_data *ddata = to_panel_data(dssdev); | ||
443 | struct omap_dss_device *in = ddata->in; | ||
444 | |||
445 | return in->ops.dpi->check_timings(in, timings); | ||
446 | } | ||
447 | |||
448 | static struct omap_dss_driver tpo_td043_ops = { | ||
449 | .connect = tpo_td043_connect, | ||
450 | .disconnect = tpo_td043_disconnect, | ||
451 | |||
452 | .enable = tpo_td043_enable, | ||
453 | .disable = tpo_td043_disable, | ||
454 | |||
455 | .set_timings = tpo_td043_set_timings, | ||
456 | .get_timings = tpo_td043_get_timings, | ||
457 | .check_timings = tpo_td043_check_timings, | ||
458 | |||
459 | .set_mirror = tpo_td043_set_hmirror, | ||
460 | .get_mirror = tpo_td043_get_hmirror, | ||
461 | |||
462 | .get_resolution = omapdss_default_get_resolution, | ||
463 | }; | ||
464 | |||
465 | |||
466 | static int tpo_td043_probe_pdata(struct spi_device *spi) | ||
467 | { | ||
468 | const struct panel_tpo_td043mtea1_platform_data *pdata; | ||
469 | struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); | ||
470 | struct omap_dss_device *dssdev, *in; | ||
471 | |||
472 | pdata = dev_get_platdata(&spi->dev); | ||
473 | |||
474 | ddata->nreset_gpio = pdata->nreset_gpio; | ||
475 | |||
476 | in = omap_dss_find_output(pdata->source); | ||
477 | if (in == NULL) { | ||
478 | dev_err(&spi->dev, "failed to find video source '%s'\n", | ||
479 | pdata->source); | ||
480 | return -EPROBE_DEFER; | ||
481 | } | ||
482 | ddata->in = in; | ||
483 | |||
484 | ddata->data_lines = pdata->data_lines; | ||
485 | |||
486 | dssdev = &ddata->dssdev; | ||
487 | dssdev->name = pdata->name; | ||
488 | |||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | static int tpo_td043_probe(struct spi_device *spi) | ||
493 | { | ||
494 | struct panel_drv_data *ddata; | ||
495 | struct omap_dss_device *dssdev; | ||
496 | int r; | ||
497 | |||
498 | dev_dbg(&spi->dev, "%s\n", __func__); | ||
499 | |||
500 | spi->bits_per_word = 16; | ||
501 | spi->mode = SPI_MODE_0; | ||
502 | |||
503 | r = spi_setup(spi); | ||
504 | if (r < 0) { | ||
505 | dev_err(&spi->dev, "spi_setup failed: %d\n", r); | ||
506 | return r; | ||
507 | } | ||
508 | |||
509 | ddata = devm_kzalloc(&spi->dev, sizeof(*ddata), GFP_KERNEL); | ||
510 | if (ddata == NULL) | ||
511 | return -ENOMEM; | ||
512 | |||
513 | dev_set_drvdata(&spi->dev, ddata); | ||
514 | |||
515 | ddata->spi = spi; | ||
516 | |||
517 | if (dev_get_platdata(&spi->dev)) { | ||
518 | r = tpo_td043_probe_pdata(spi); | ||
519 | if (r) | ||
520 | return r; | ||
521 | } else { | ||
522 | return -ENODEV; | ||
523 | } | ||
524 | |||
525 | ddata->mode = TPO_R02_MODE_800x480; | ||
526 | memcpy(ddata->gamma, tpo_td043_def_gamma, sizeof(ddata->gamma)); | ||
527 | |||
528 | ddata->vcc_reg = devm_regulator_get(&spi->dev, "vcc"); | ||
529 | if (IS_ERR(ddata->vcc_reg)) { | ||
530 | dev_err(&spi->dev, "failed to get LCD VCC regulator\n"); | ||
531 | r = PTR_ERR(ddata->vcc_reg); | ||
532 | goto err_regulator; | ||
533 | } | ||
534 | |||
535 | if (gpio_is_valid(ddata->nreset_gpio)) { | ||
536 | r = devm_gpio_request_one(&spi->dev, | ||
537 | ddata->nreset_gpio, GPIOF_OUT_INIT_LOW, | ||
538 | "lcd reset"); | ||
539 | if (r < 0) { | ||
540 | dev_err(&spi->dev, "couldn't request reset GPIO\n"); | ||
541 | goto err_gpio_req; | ||
542 | } | ||
543 | } | ||
544 | |||
545 | r = sysfs_create_group(&spi->dev.kobj, &tpo_td043_attr_group); | ||
546 | if (r) { | ||
547 | dev_err(&spi->dev, "failed to create sysfs files\n"); | ||
548 | goto err_sysfs; | ||
549 | } | ||
550 | |||
551 | ddata->videomode = tpo_td043_timings; | ||
552 | |||
553 | dssdev = &ddata->dssdev; | ||
554 | dssdev->dev = &spi->dev; | ||
555 | dssdev->driver = &tpo_td043_ops; | ||
556 | dssdev->type = OMAP_DISPLAY_TYPE_DPI; | ||
557 | dssdev->owner = THIS_MODULE; | ||
558 | dssdev->panel.timings = ddata->videomode; | ||
559 | |||
560 | r = omapdss_register_display(dssdev); | ||
561 | if (r) { | ||
562 | dev_err(&spi->dev, "Failed to register panel\n"); | ||
563 | goto err_reg; | ||
564 | } | ||
565 | |||
566 | return 0; | ||
567 | |||
568 | err_reg: | ||
569 | sysfs_remove_group(&spi->dev.kobj, &tpo_td043_attr_group); | ||
570 | err_sysfs: | ||
571 | err_gpio_req: | ||
572 | err_regulator: | ||
573 | omap_dss_put_device(ddata->in); | ||
574 | return r; | ||
575 | } | ||
576 | |||
577 | static int tpo_td043_remove(struct spi_device *spi) | ||
578 | { | ||
579 | struct panel_drv_data *ddata = dev_get_drvdata(&spi->dev); | ||
580 | struct omap_dss_device *dssdev = &ddata->dssdev; | ||
581 | struct omap_dss_device *in = ddata->in; | ||
582 | |||
583 | dev_dbg(&ddata->spi->dev, "%s\n", __func__); | ||
584 | |||
585 | omapdss_unregister_display(dssdev); | ||
586 | |||
587 | tpo_td043_disable(dssdev); | ||
588 | tpo_td043_disconnect(dssdev); | ||
589 | |||
590 | omap_dss_put_device(in); | ||
591 | |||
592 | sysfs_remove_group(&spi->dev.kobj, &tpo_td043_attr_group); | ||
593 | |||
594 | return 0; | ||
595 | } | ||
596 | |||
597 | #ifdef CONFIG_PM_SLEEP | ||
598 | static int tpo_td043_spi_suspend(struct device *dev) | ||
599 | { | ||
600 | struct panel_drv_data *ddata = dev_get_drvdata(dev); | ||
601 | |||
602 | dev_dbg(dev, "tpo_td043_spi_suspend, tpo %p\n", ddata); | ||
603 | |||
604 | ddata->power_on_resume = ddata->powered_on; | ||
605 | tpo_td043_power_off(ddata); | ||
606 | ddata->spi_suspended = 1; | ||
607 | |||
608 | return 0; | ||
609 | } | ||
610 | |||
611 | static int tpo_td043_spi_resume(struct device *dev) | ||
612 | { | ||
613 | struct panel_drv_data *ddata = dev_get_drvdata(dev); | ||
614 | int ret; | ||
615 | |||
616 | dev_dbg(dev, "tpo_td043_spi_resume\n"); | ||
617 | |||
618 | if (ddata->power_on_resume) { | ||
619 | ret = tpo_td043_power_on(ddata); | ||
620 | if (ret) | ||
621 | return ret; | ||
622 | } | ||
623 | ddata->spi_suspended = 0; | ||
624 | |||
625 | return 0; | ||
626 | } | ||
627 | #endif | ||
628 | |||
629 | static SIMPLE_DEV_PM_OPS(tpo_td043_spi_pm, | ||
630 | tpo_td043_spi_suspend, tpo_td043_spi_resume); | ||
631 | |||
632 | static struct spi_driver tpo_td043_spi_driver = { | ||
633 | .driver = { | ||
634 | .name = "panel-tpo-td043mtea1", | ||
635 | .owner = THIS_MODULE, | ||
636 | .pm = &tpo_td043_spi_pm, | ||
637 | }, | ||
638 | .probe = tpo_td043_probe, | ||
639 | .remove = tpo_td043_remove, | ||
640 | }; | ||
641 | |||
642 | module_spi_driver(tpo_td043_spi_driver); | ||
643 | |||
644 | MODULE_AUTHOR("Gražvydas Ignotas <notasas@gmail.com>"); | ||
645 | MODULE_DESCRIPTION("TPO TD043MTEA1 LCD Driver"); | ||
646 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/omap2/displays/Kconfig b/drivers/video/omap2/displays/Kconfig index c3853c92279b..e80ac1c79561 100644 --- a/drivers/video/omap2/displays/Kconfig +++ b/drivers/video/omap2/displays/Kconfig | |||
@@ -1,4 +1,4 @@ | |||
1 | menu "OMAP2/3 Display Device Drivers" | 1 | menu "OMAP2/3 Display Device Drivers (old device model)" |
2 | depends on OMAP2_DSS | 2 | depends on OMAP2_DSS |
3 | 3 | ||
4 | config PANEL_GENERIC_DPI | 4 | config PANEL_GENERIC_DPI |
diff --git a/drivers/video/omap2/dss/apply.c b/drivers/video/omap2/dss/apply.c index 752b98592908..d6212d63cfb2 100644 --- a/drivers/video/omap2/dss/apply.c +++ b/drivers/video/omap2/dss/apply.c | |||
@@ -422,7 +422,19 @@ static void wait_pending_extra_info_updates(void) | |||
422 | 422 | ||
423 | static struct omap_dss_device *dss_mgr_get_device(struct omap_overlay_manager *mgr) | 423 | static struct omap_dss_device *dss_mgr_get_device(struct omap_overlay_manager *mgr) |
424 | { | 424 | { |
425 | return mgr->output ? mgr->output->device : NULL; | 425 | struct omap_dss_device *dssdev; |
426 | |||
427 | dssdev = mgr->output; | ||
428 | if (dssdev == NULL) | ||
429 | return NULL; | ||
430 | |||
431 | while (dssdev->device) | ||
432 | dssdev = dssdev->device; | ||
433 | |||
434 | if (dssdev->driver) | ||
435 | return dssdev; | ||
436 | else | ||
437 | return NULL; | ||
426 | } | 438 | } |
427 | 439 | ||
428 | static struct omap_dss_device *dss_ovl_get_device(struct omap_overlay *ovl) | 440 | static struct omap_dss_device *dss_ovl_get_device(struct omap_overlay *ovl) |
diff --git a/drivers/video/omap2/dss/display.c b/drivers/video/omap2/dss/display.c index 0daf3e37d597..fafe7c941a60 100644 --- a/drivers/video/omap2/dss/display.c +++ b/drivers/video/omap2/dss/display.c | |||
@@ -61,6 +61,7 @@ int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev) | |||
61 | case OMAP_DISPLAY_TYPE_VENC: | 61 | case OMAP_DISPLAY_TYPE_VENC: |
62 | case OMAP_DISPLAY_TYPE_SDI: | 62 | case OMAP_DISPLAY_TYPE_SDI: |
63 | case OMAP_DISPLAY_TYPE_HDMI: | 63 | case OMAP_DISPLAY_TYPE_HDMI: |
64 | case OMAP_DISPLAY_TYPE_DVI: | ||
64 | return 24; | 65 | return 24; |
65 | default: | 66 | default: |
66 | BUG(); | 67 | BUG(); |
diff --git a/drivers/video/omap2/dss/dpi.c b/drivers/video/omap2/dss/dpi.c index 892a2b207ee4..6433eab6bcf2 100644 --- a/drivers/video/omap2/dss/dpi.c +++ b/drivers/video/omap2/dss/dpi.c | |||
@@ -461,6 +461,16 @@ void omapdss_dpi_set_timings(struct omap_dss_device *dssdev, | |||
461 | } | 461 | } |
462 | EXPORT_SYMBOL(omapdss_dpi_set_timings); | 462 | EXPORT_SYMBOL(omapdss_dpi_set_timings); |
463 | 463 | ||
464 | static void dpi_get_timings(struct omap_dss_device *dssdev, | ||
465 | struct omap_video_timings *timings) | ||
466 | { | ||
467 | mutex_lock(&dpi.lock); | ||
468 | |||
469 | *timings = dpi.timings; | ||
470 | |||
471 | mutex_unlock(&dpi.lock); | ||
472 | } | ||
473 | |||
464 | int dpi_check_timings(struct omap_dss_device *dssdev, | 474 | int dpi_check_timings(struct omap_dss_device *dssdev, |
465 | struct omap_video_timings *timings) | 475 | struct omap_video_timings *timings) |
466 | { | 476 | { |
@@ -678,6 +688,65 @@ static int dpi_probe_pdata(struct platform_device *dpidev) | |||
678 | return 0; | 688 | return 0; |
679 | } | 689 | } |
680 | 690 | ||
691 | static int dpi_connect(struct omap_dss_device *dssdev, | ||
692 | struct omap_dss_device *dst) | ||
693 | { | ||
694 | struct omap_overlay_manager *mgr; | ||
695 | int r; | ||
696 | |||
697 | r = dpi_init_regulator(); | ||
698 | if (r) | ||
699 | return r; | ||
700 | |||
701 | dpi_init_pll(); | ||
702 | |||
703 | mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); | ||
704 | if (!mgr) | ||
705 | return -ENODEV; | ||
706 | |||
707 | r = dss_mgr_connect(mgr, dssdev); | ||
708 | if (r) | ||
709 | return r; | ||
710 | |||
711 | r = omapdss_output_set_device(dssdev, dst); | ||
712 | if (r) { | ||
713 | DSSERR("failed to connect output to new device: %s\n", | ||
714 | dst->name); | ||
715 | dss_mgr_disconnect(mgr, dssdev); | ||
716 | return r; | ||
717 | } | ||
718 | |||
719 | return 0; | ||
720 | } | ||
721 | |||
722 | static void dpi_disconnect(struct omap_dss_device *dssdev, | ||
723 | struct omap_dss_device *dst) | ||
724 | { | ||
725 | WARN_ON(dst != dssdev->device); | ||
726 | |||
727 | if (dst != dssdev->device) | ||
728 | return; | ||
729 | |||
730 | omapdss_output_unset_device(dssdev); | ||
731 | |||
732 | if (dssdev->manager) | ||
733 | dss_mgr_disconnect(dssdev->manager, dssdev); | ||
734 | } | ||
735 | |||
736 | static const struct omapdss_dpi_ops dpi_ops = { | ||
737 | .connect = dpi_connect, | ||
738 | .disconnect = dpi_disconnect, | ||
739 | |||
740 | .enable = omapdss_dpi_display_enable, | ||
741 | .disable = omapdss_dpi_display_disable, | ||
742 | |||
743 | .check_timings = dpi_check_timings, | ||
744 | .set_timings = omapdss_dpi_set_timings, | ||
745 | .get_timings = dpi_get_timings, | ||
746 | |||
747 | .set_data_lines = omapdss_dpi_set_data_lines, | ||
748 | }; | ||
749 | |||
681 | static void dpi_init_output(struct platform_device *pdev) | 750 | static void dpi_init_output(struct platform_device *pdev) |
682 | { | 751 | { |
683 | struct omap_dss_device *out = &dpi.output; | 752 | struct omap_dss_device *out = &dpi.output; |
@@ -687,16 +756,17 @@ static void dpi_init_output(struct platform_device *pdev) | |||
687 | out->output_type = OMAP_DISPLAY_TYPE_DPI; | 756 | out->output_type = OMAP_DISPLAY_TYPE_DPI; |
688 | out->name = "dpi.0"; | 757 | out->name = "dpi.0"; |
689 | out->dispc_channel = dpi_get_channel(); | 758 | out->dispc_channel = dpi_get_channel(); |
759 | out->ops.dpi = &dpi_ops; | ||
690 | out->owner = THIS_MODULE; | 760 | out->owner = THIS_MODULE; |
691 | 761 | ||
692 | dss_register_output(out); | 762 | omapdss_register_output(out); |
693 | } | 763 | } |
694 | 764 | ||
695 | static void __exit dpi_uninit_output(struct platform_device *pdev) | 765 | static void __exit dpi_uninit_output(struct platform_device *pdev) |
696 | { | 766 | { |
697 | struct omap_dss_device *out = &dpi.output; | 767 | struct omap_dss_device *out = &dpi.output; |
698 | 768 | ||
699 | dss_unregister_output(out); | 769 | omapdss_unregister_output(out); |
700 | } | 770 | } |
701 | 771 | ||
702 | static int omap_dpi_probe(struct platform_device *pdev) | 772 | static int omap_dpi_probe(struct platform_device *pdev) |
diff --git a/drivers/video/omap2/dss/dsi.c b/drivers/video/omap2/dss/dsi.c index d6b019faed23..99a043b08f0d 100644 --- a/drivers/video/omap2/dss/dsi.c +++ b/drivers/video/omap2/dss/dsi.c | |||
@@ -383,6 +383,15 @@ static inline struct dsi_data *dsi_get_dsidrv_data(struct platform_device *dside | |||
383 | 383 | ||
384 | static inline struct platform_device *dsi_get_dsidev_from_dssdev(struct omap_dss_device *dssdev) | 384 | static inline struct platform_device *dsi_get_dsidev_from_dssdev(struct omap_dss_device *dssdev) |
385 | { | 385 | { |
386 | /* HACK: dssdev can be either the panel device, when using old API, or | ||
387 | * the dsi device itself, when using the new API. So we solve this for | ||
388 | * now by checking the dssdev->id. This will be removed when the old API | ||
389 | * is removed. | ||
390 | */ | ||
391 | if (dssdev->id == OMAP_DSS_OUTPUT_DSI1 || | ||
392 | dssdev->id == OMAP_DSS_OUTPUT_DSI2) | ||
393 | return to_platform_device(dssdev->dev); | ||
394 | |||
386 | return to_platform_device(dssdev->output->dev); | 395 | return to_platform_device(dssdev->output->dev); |
387 | } | 396 | } |
388 | 397 | ||
@@ -5412,6 +5421,89 @@ static int dsi_probe_pdata(struct platform_device *dsidev) | |||
5412 | return 0; | 5421 | return 0; |
5413 | } | 5422 | } |
5414 | 5423 | ||
5424 | static int dsi_connect(struct omap_dss_device *dssdev, | ||
5425 | struct omap_dss_device *dst) | ||
5426 | { | ||
5427 | struct platform_device *dsidev = dsi_get_dsidev_from_dssdev(dssdev); | ||
5428 | struct omap_overlay_manager *mgr; | ||
5429 | int r; | ||
5430 | |||
5431 | r = dsi_regulator_init(dsidev); | ||
5432 | if (r) | ||
5433 | return r; | ||
5434 | |||
5435 | mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); | ||
5436 | if (!mgr) | ||
5437 | return -ENODEV; | ||
5438 | |||
5439 | r = dss_mgr_connect(mgr, dssdev); | ||
5440 | if (r) | ||
5441 | return r; | ||
5442 | |||
5443 | r = omapdss_output_set_device(dssdev, dst); | ||
5444 | if (r) { | ||
5445 | DSSERR("failed to connect output to new device: %s\n", | ||
5446 | dssdev->name); | ||
5447 | dss_mgr_disconnect(mgr, dssdev); | ||
5448 | return r; | ||
5449 | } | ||
5450 | |||
5451 | return 0; | ||
5452 | } | ||
5453 | |||
5454 | static void dsi_disconnect(struct omap_dss_device *dssdev, | ||
5455 | struct omap_dss_device *dst) | ||
5456 | { | ||
5457 | WARN_ON(dst != dssdev->device); | ||
5458 | |||
5459 | if (dst != dssdev->device) | ||
5460 | return; | ||
5461 | |||
5462 | omapdss_output_unset_device(dssdev); | ||
5463 | |||
5464 | if (dssdev->manager) | ||
5465 | dss_mgr_disconnect(dssdev->manager, dssdev); | ||
5466 | } | ||
5467 | |||
5468 | static const struct omapdss_dsi_ops dsi_ops = { | ||
5469 | .connect = dsi_connect, | ||
5470 | .disconnect = dsi_disconnect, | ||
5471 | |||
5472 | .bus_lock = dsi_bus_lock, | ||
5473 | .bus_unlock = dsi_bus_unlock, | ||
5474 | |||
5475 | .enable = omapdss_dsi_display_enable, | ||
5476 | .disable = omapdss_dsi_display_disable, | ||
5477 | |||
5478 | .enable_hs = omapdss_dsi_vc_enable_hs, | ||
5479 | |||
5480 | .configure_pins = omapdss_dsi_configure_pins, | ||
5481 | .set_config = omapdss_dsi_set_config, | ||
5482 | |||
5483 | .enable_video_output = dsi_enable_video_output, | ||
5484 | .disable_video_output = dsi_disable_video_output, | ||
5485 | |||
5486 | .update = omap_dsi_update, | ||
5487 | |||
5488 | .enable_te = omapdss_dsi_enable_te, | ||
5489 | |||
5490 | .request_vc = omap_dsi_request_vc, | ||
5491 | .set_vc_id = omap_dsi_set_vc_id, | ||
5492 | .release_vc = omap_dsi_release_vc, | ||
5493 | |||
5494 | .dcs_write = dsi_vc_dcs_write, | ||
5495 | .dcs_write_nosync = dsi_vc_dcs_write_nosync, | ||
5496 | .dcs_read = dsi_vc_dcs_read, | ||
5497 | |||
5498 | .gen_write = dsi_vc_generic_write, | ||
5499 | .gen_write_nosync = dsi_vc_generic_write_nosync, | ||
5500 | .gen_read = dsi_vc_generic_read, | ||
5501 | |||
5502 | .bta_sync = dsi_vc_send_bta_sync, | ||
5503 | |||
5504 | .set_max_rx_packet_size = dsi_vc_set_max_rx_packet_size, | ||
5505 | }; | ||
5506 | |||
5415 | static void dsi_init_output(struct platform_device *dsidev) | 5507 | static void dsi_init_output(struct platform_device *dsidev) |
5416 | { | 5508 | { |
5417 | struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); | 5509 | struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); |
@@ -5424,9 +5516,10 @@ static void dsi_init_output(struct platform_device *dsidev) | |||
5424 | out->output_type = OMAP_DISPLAY_TYPE_DSI; | 5516 | out->output_type = OMAP_DISPLAY_TYPE_DSI; |
5425 | out->name = dsi->module_id == 0 ? "dsi.0" : "dsi.1"; | 5517 | out->name = dsi->module_id == 0 ? "dsi.0" : "dsi.1"; |
5426 | out->dispc_channel = dsi_get_channel(dsi->module_id); | 5518 | out->dispc_channel = dsi_get_channel(dsi->module_id); |
5519 | out->ops.dsi = &dsi_ops; | ||
5427 | out->owner = THIS_MODULE; | 5520 | out->owner = THIS_MODULE; |
5428 | 5521 | ||
5429 | dss_register_output(out); | 5522 | omapdss_register_output(out); |
5430 | } | 5523 | } |
5431 | 5524 | ||
5432 | static void dsi_uninit_output(struct platform_device *dsidev) | 5525 | static void dsi_uninit_output(struct platform_device *dsidev) |
@@ -5434,7 +5527,7 @@ static void dsi_uninit_output(struct platform_device *dsidev) | |||
5434 | struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); | 5527 | struct dsi_data *dsi = dsi_get_dsidrv_data(dsidev); |
5435 | struct omap_dss_device *out = &dsi->output; | 5528 | struct omap_dss_device *out = &dsi->output; |
5436 | 5529 | ||
5437 | dss_unregister_output(out); | 5530 | omapdss_unregister_output(out); |
5438 | } | 5531 | } |
5439 | 5532 | ||
5440 | /* DSI1 HW IP initialisation */ | 5533 | /* DSI1 HW IP initialisation */ |
diff --git a/drivers/video/omap2/dss/dss.h b/drivers/video/omap2/dss/dss.h index ed70fa01d192..50a2362ef8f8 100644 --- a/drivers/video/omap2/dss/dss.h +++ b/drivers/video/omap2/dss/dss.h | |||
@@ -179,10 +179,6 @@ void dss_put_device(struct omap_dss_device *dssdev); | |||
179 | void dss_copy_device_pdata(struct omap_dss_device *dst, | 179 | void dss_copy_device_pdata(struct omap_dss_device *dst, |
180 | const struct omap_dss_device *src); | 180 | const struct omap_dss_device *src); |
181 | 181 | ||
182 | /* output */ | ||
183 | void dss_register_output(struct omap_dss_device *out); | ||
184 | void dss_unregister_output(struct omap_dss_device *out); | ||
185 | |||
186 | /* display */ | 182 | /* display */ |
187 | int dss_suspend_all_devices(void); | 183 | int dss_suspend_all_devices(void); |
188 | int dss_resume_all_devices(void); | 184 | int dss_resume_all_devices(void); |
diff --git a/drivers/video/omap2/dss/hdmi.c b/drivers/video/omap2/dss/hdmi.c index e1c0992b522b..44a885b92825 100644 --- a/drivers/video/omap2/dss/hdmi.c +++ b/drivers/video/omap2/dss/hdmi.c | |||
@@ -70,6 +70,8 @@ static struct { | |||
70 | int ls_oe_gpio; | 70 | int ls_oe_gpio; |
71 | int hpd_gpio; | 71 | int hpd_gpio; |
72 | 72 | ||
73 | bool core_enabled; | ||
74 | |||
73 | struct omap_dss_device output; | 75 | struct omap_dss_device output; |
74 | } hdmi; | 76 | } hdmi; |
75 | 77 | ||
@@ -515,8 +517,10 @@ static int hdmi_power_on_core(struct omap_dss_device *dssdev) | |||
515 | { | 517 | { |
516 | int r; | 518 | int r; |
517 | 519 | ||
518 | gpio_set_value(hdmi.ct_cp_hpd_gpio, 1); | 520 | if (gpio_is_valid(hdmi.ct_cp_hpd_gpio)) |
519 | gpio_set_value(hdmi.ls_oe_gpio, 1); | 521 | gpio_set_value(hdmi.ct_cp_hpd_gpio, 1); |
522 | if (gpio_is_valid(hdmi.ls_oe_gpio)) | ||
523 | gpio_set_value(hdmi.ls_oe_gpio, 1); | ||
520 | 524 | ||
521 | /* wait 300us after CT_CP_HPD for the 5V power output to reach 90% */ | 525 | /* wait 300us after CT_CP_HPD for the 5V power output to reach 90% */ |
522 | udelay(300); | 526 | udelay(300); |
@@ -532,22 +536,30 @@ static int hdmi_power_on_core(struct omap_dss_device *dssdev) | |||
532 | /* Make selection of HDMI in DSS */ | 536 | /* Make selection of HDMI in DSS */ |
533 | dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK); | 537 | dss_select_hdmi_venc_clk_source(DSS_HDMI_M_PCLK); |
534 | 538 | ||
539 | hdmi.core_enabled = true; | ||
540 | |||
535 | return 0; | 541 | return 0; |
536 | 542 | ||
537 | err_runtime_get: | 543 | err_runtime_get: |
538 | regulator_disable(hdmi.vdda_hdmi_dac_reg); | 544 | regulator_disable(hdmi.vdda_hdmi_dac_reg); |
539 | err_vdac_enable: | 545 | err_vdac_enable: |
540 | gpio_set_value(hdmi.ct_cp_hpd_gpio, 0); | 546 | if (gpio_is_valid(hdmi.ct_cp_hpd_gpio)) |
541 | gpio_set_value(hdmi.ls_oe_gpio, 0); | 547 | gpio_set_value(hdmi.ct_cp_hpd_gpio, 0); |
548 | if (gpio_is_valid(hdmi.ls_oe_gpio)) | ||
549 | gpio_set_value(hdmi.ls_oe_gpio, 0); | ||
542 | return r; | 550 | return r; |
543 | } | 551 | } |
544 | 552 | ||
545 | static void hdmi_power_off_core(struct omap_dss_device *dssdev) | 553 | static void hdmi_power_off_core(struct omap_dss_device *dssdev) |
546 | { | 554 | { |
555 | hdmi.core_enabled = false; | ||
556 | |||
547 | hdmi_runtime_put(); | 557 | hdmi_runtime_put(); |
548 | regulator_disable(hdmi.vdda_hdmi_dac_reg); | 558 | regulator_disable(hdmi.vdda_hdmi_dac_reg); |
549 | gpio_set_value(hdmi.ct_cp_hpd_gpio, 0); | 559 | if (gpio_is_valid(hdmi.ct_cp_hpd_gpio)) |
550 | gpio_set_value(hdmi.ls_oe_gpio, 0); | 560 | gpio_set_value(hdmi.ct_cp_hpd_gpio, 0); |
561 | if (gpio_is_valid(hdmi.ls_oe_gpio)) | ||
562 | gpio_set_value(hdmi.ls_oe_gpio, 0); | ||
551 | } | 563 | } |
552 | 564 | ||
553 | static int hdmi_power_on_full(struct omap_dss_device *dssdev) | 565 | static int hdmi_power_on_full(struct omap_dss_device *dssdev) |
@@ -662,6 +674,18 @@ void omapdss_hdmi_display_set_timing(struct omap_dss_device *dssdev, | |||
662 | mutex_unlock(&hdmi.lock); | 674 | mutex_unlock(&hdmi.lock); |
663 | } | 675 | } |
664 | 676 | ||
677 | static void omapdss_hdmi_display_get_timings(struct omap_dss_device *dssdev, | ||
678 | struct omap_video_timings *timings) | ||
679 | { | ||
680 | const struct hdmi_config *cfg; | ||
681 | |||
682 | cfg = hdmi_get_timings(); | ||
683 | if (cfg == NULL) | ||
684 | cfg = &vesa_timings[0]; | ||
685 | |||
686 | memcpy(timings, &cfg->timings, sizeof(cfg->timings)); | ||
687 | } | ||
688 | |||
665 | static void hdmi_dump_regs(struct seq_file *s) | 689 | static void hdmi_dump_regs(struct seq_file *s) |
666 | { | 690 | { |
667 | mutex_lock(&hdmi.lock); | 691 | mutex_lock(&hdmi.lock); |
@@ -1025,6 +1049,199 @@ static int hdmi_probe_pdata(struct platform_device *pdev) | |||
1025 | return 0; | 1049 | return 0; |
1026 | } | 1050 | } |
1027 | 1051 | ||
1052 | static int hdmi_connect(struct omap_dss_device *dssdev, | ||
1053 | struct omap_dss_device *dst) | ||
1054 | { | ||
1055 | struct omap_overlay_manager *mgr; | ||
1056 | int r; | ||
1057 | |||
1058 | dss_init_hdmi_ip_ops(&hdmi.ip_data, omapdss_get_version()); | ||
1059 | |||
1060 | r = hdmi_init_regulator(); | ||
1061 | if (r) | ||
1062 | return r; | ||
1063 | |||
1064 | mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); | ||
1065 | if (!mgr) | ||
1066 | return -ENODEV; | ||
1067 | |||
1068 | r = dss_mgr_connect(mgr, dssdev); | ||
1069 | if (r) | ||
1070 | return r; | ||
1071 | |||
1072 | r = omapdss_output_set_device(dssdev, dst); | ||
1073 | if (r) { | ||
1074 | DSSERR("failed to connect output to new device: %s\n", | ||
1075 | dst->name); | ||
1076 | dss_mgr_disconnect(mgr, dssdev); | ||
1077 | return r; | ||
1078 | } | ||
1079 | |||
1080 | return 0; | ||
1081 | } | ||
1082 | |||
1083 | static void hdmi_disconnect(struct omap_dss_device *dssdev, | ||
1084 | struct omap_dss_device *dst) | ||
1085 | { | ||
1086 | WARN_ON(dst != dssdev->device); | ||
1087 | |||
1088 | if (dst != dssdev->device) | ||
1089 | return; | ||
1090 | |||
1091 | omapdss_output_unset_device(dssdev); | ||
1092 | |||
1093 | if (dssdev->manager) | ||
1094 | dss_mgr_disconnect(dssdev->manager, dssdev); | ||
1095 | } | ||
1096 | |||
1097 | static int hdmi_read_edid(struct omap_dss_device *dssdev, | ||
1098 | u8 *edid, int len) | ||
1099 | { | ||
1100 | bool need_enable; | ||
1101 | int r; | ||
1102 | |||
1103 | need_enable = hdmi.core_enabled == false; | ||
1104 | |||
1105 | if (need_enable) { | ||
1106 | r = omapdss_hdmi_core_enable(dssdev); | ||
1107 | if (r) | ||
1108 | return r; | ||
1109 | } | ||
1110 | |||
1111 | r = omapdss_hdmi_read_edid(edid, len); | ||
1112 | |||
1113 | if (need_enable) | ||
1114 | omapdss_hdmi_core_disable(dssdev); | ||
1115 | |||
1116 | return r; | ||
1117 | } | ||
1118 | |||
1119 | #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) | ||
1120 | static int omapdss_hdmi_audio_enable(struct omap_dss_device *dssdev) | ||
1121 | { | ||
1122 | int r; | ||
1123 | |||
1124 | mutex_lock(&hdmi.lock); | ||
1125 | |||
1126 | if (!hdmi_mode_has_audio()) { | ||
1127 | r = -EPERM; | ||
1128 | goto err; | ||
1129 | } | ||
1130 | |||
1131 | r = hdmi_audio_enable(); | ||
1132 | if (r) | ||
1133 | goto err; | ||
1134 | |||
1135 | mutex_unlock(&hdmi.lock); | ||
1136 | return 0; | ||
1137 | |||
1138 | err: | ||
1139 | mutex_unlock(&hdmi.lock); | ||
1140 | return r; | ||
1141 | } | ||
1142 | |||
1143 | static void omapdss_hdmi_audio_disable(struct omap_dss_device *dssdev) | ||
1144 | { | ||
1145 | hdmi_audio_disable(); | ||
1146 | } | ||
1147 | |||
1148 | static int omapdss_hdmi_audio_start(struct omap_dss_device *dssdev) | ||
1149 | { | ||
1150 | return hdmi_audio_start(); | ||
1151 | } | ||
1152 | |||
1153 | static void omapdss_hdmi_audio_stop(struct omap_dss_device *dssdev) | ||
1154 | { | ||
1155 | hdmi_audio_stop(); | ||
1156 | } | ||
1157 | |||
1158 | static bool omapdss_hdmi_audio_supported(struct omap_dss_device *dssdev) | ||
1159 | { | ||
1160 | bool r; | ||
1161 | |||
1162 | mutex_lock(&hdmi.lock); | ||
1163 | |||
1164 | r = hdmi_mode_has_audio(); | ||
1165 | |||
1166 | mutex_unlock(&hdmi.lock); | ||
1167 | return r; | ||
1168 | } | ||
1169 | |||
1170 | static int omapdss_hdmi_audio_config(struct omap_dss_device *dssdev, | ||
1171 | struct omap_dss_audio *audio) | ||
1172 | { | ||
1173 | int r; | ||
1174 | |||
1175 | mutex_lock(&hdmi.lock); | ||
1176 | |||
1177 | if (!hdmi_mode_has_audio()) { | ||
1178 | r = -EPERM; | ||
1179 | goto err; | ||
1180 | } | ||
1181 | |||
1182 | r = hdmi_audio_config(audio); | ||
1183 | if (r) | ||
1184 | goto err; | ||
1185 | |||
1186 | mutex_unlock(&hdmi.lock); | ||
1187 | return 0; | ||
1188 | |||
1189 | err: | ||
1190 | mutex_unlock(&hdmi.lock); | ||
1191 | return r; | ||
1192 | } | ||
1193 | #else | ||
1194 | static int omapdss_hdmi_audio_enable(struct omap_dss_device *dssdev) | ||
1195 | { | ||
1196 | return -EPERM; | ||
1197 | } | ||
1198 | |||
1199 | static void omapdss_hdmi_audio_disable(struct omap_dss_device *dssdev) | ||
1200 | { | ||
1201 | } | ||
1202 | |||
1203 | static int omapdss_hdmi_audio_start(struct omap_dss_device *dssdev) | ||
1204 | { | ||
1205 | return -EPERM; | ||
1206 | } | ||
1207 | |||
1208 | static void omapdss_hdmi_audio_stop(struct omap_dss_device *dssdev) | ||
1209 | { | ||
1210 | } | ||
1211 | |||
1212 | static bool omapdss_hdmi_audio_supported(struct omap_dss_device *dssdev) | ||
1213 | { | ||
1214 | return false; | ||
1215 | } | ||
1216 | |||
1217 | static int omapdss_hdmi_audio_config(struct omap_dss_device *dssdev, | ||
1218 | struct omap_dss_audio *audio) | ||
1219 | { | ||
1220 | return -EPERM; | ||
1221 | } | ||
1222 | #endif | ||
1223 | |||
1224 | static const struct omapdss_hdmi_ops hdmi_ops = { | ||
1225 | .connect = hdmi_connect, | ||
1226 | .disconnect = hdmi_disconnect, | ||
1227 | |||
1228 | .enable = omapdss_hdmi_display_enable, | ||
1229 | .disable = omapdss_hdmi_display_disable, | ||
1230 | |||
1231 | .check_timings = omapdss_hdmi_display_check_timing, | ||
1232 | .set_timings = omapdss_hdmi_display_set_timing, | ||
1233 | .get_timings = omapdss_hdmi_display_get_timings, | ||
1234 | |||
1235 | .read_edid = hdmi_read_edid, | ||
1236 | |||
1237 | .audio_enable = omapdss_hdmi_audio_enable, | ||
1238 | .audio_disable = omapdss_hdmi_audio_disable, | ||
1239 | .audio_start = omapdss_hdmi_audio_start, | ||
1240 | .audio_stop = omapdss_hdmi_audio_stop, | ||
1241 | .audio_supported = omapdss_hdmi_audio_supported, | ||
1242 | .audio_config = omapdss_hdmi_audio_config, | ||
1243 | }; | ||
1244 | |||
1028 | static void hdmi_init_output(struct platform_device *pdev) | 1245 | static void hdmi_init_output(struct platform_device *pdev) |
1029 | { | 1246 | { |
1030 | struct omap_dss_device *out = &hdmi.output; | 1247 | struct omap_dss_device *out = &hdmi.output; |
@@ -1034,16 +1251,17 @@ static void hdmi_init_output(struct platform_device *pdev) | |||
1034 | out->output_type = OMAP_DISPLAY_TYPE_HDMI; | 1251 | out->output_type = OMAP_DISPLAY_TYPE_HDMI; |
1035 | out->name = "hdmi.0"; | 1252 | out->name = "hdmi.0"; |
1036 | out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; | 1253 | out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; |
1254 | out->ops.hdmi = &hdmi_ops; | ||
1037 | out->owner = THIS_MODULE; | 1255 | out->owner = THIS_MODULE; |
1038 | 1256 | ||
1039 | dss_register_output(out); | 1257 | omapdss_register_output(out); |
1040 | } | 1258 | } |
1041 | 1259 | ||
1042 | static void __exit hdmi_uninit_output(struct platform_device *pdev) | 1260 | static void __exit hdmi_uninit_output(struct platform_device *pdev) |
1043 | { | 1261 | { |
1044 | struct omap_dss_device *out = &hdmi.output; | 1262 | struct omap_dss_device *out = &hdmi.output; |
1045 | 1263 | ||
1046 | dss_unregister_output(out); | 1264 | omapdss_unregister_output(out); |
1047 | } | 1265 | } |
1048 | 1266 | ||
1049 | /* HDMI HW IP initialisation */ | 1267 | /* HDMI HW IP initialisation */ |
@@ -1083,6 +1301,10 @@ static int omapdss_hdmihw_probe(struct platform_device *pdev) | |||
1083 | hdmi.ip_data.pll_offset = HDMI_PLLCTRL; | 1301 | hdmi.ip_data.pll_offset = HDMI_PLLCTRL; |
1084 | hdmi.ip_data.phy_offset = HDMI_PHY; | 1302 | hdmi.ip_data.phy_offset = HDMI_PHY; |
1085 | 1303 | ||
1304 | hdmi.ct_cp_hpd_gpio = -1; | ||
1305 | hdmi.ls_oe_gpio = -1; | ||
1306 | hdmi.hpd_gpio = -1; | ||
1307 | |||
1086 | hdmi_init_output(pdev); | 1308 | hdmi_init_output(pdev); |
1087 | 1309 | ||
1088 | r = hdmi_panel_init(); | 1310 | r = hdmi_panel_init(); |
diff --git a/drivers/video/omap2/dss/output.c b/drivers/video/omap2/dss/output.c index 9ad7d2175a7b..3f5c0a758b32 100644 --- a/drivers/video/omap2/dss/output.c +++ b/drivers/video/omap2/dss/output.c | |||
@@ -92,15 +92,18 @@ err: | |||
92 | } | 92 | } |
93 | EXPORT_SYMBOL(omapdss_output_unset_device); | 93 | EXPORT_SYMBOL(omapdss_output_unset_device); |
94 | 94 | ||
95 | void dss_register_output(struct omap_dss_device *out) | 95 | int omapdss_register_output(struct omap_dss_device *out) |
96 | { | 96 | { |
97 | list_add_tail(&out->list, &output_list); | 97 | list_add_tail(&out->list, &output_list); |
98 | return 0; | ||
98 | } | 99 | } |
100 | EXPORT_SYMBOL(omapdss_register_output); | ||
99 | 101 | ||
100 | void dss_unregister_output(struct omap_dss_device *out) | 102 | void omapdss_unregister_output(struct omap_dss_device *out) |
101 | { | 103 | { |
102 | list_del(&out->list); | 104 | list_del(&out->list); |
103 | } | 105 | } |
106 | EXPORT_SYMBOL(omapdss_unregister_output); | ||
104 | 107 | ||
105 | struct omap_dss_device *omap_dss_get_output(enum omap_dss_output_id id) | 108 | struct omap_dss_device *omap_dss_get_output(enum omap_dss_output_id id) |
106 | { | 109 | { |
@@ -143,7 +146,13 @@ EXPORT_SYMBOL(omap_dss_find_output_by_node); | |||
143 | 146 | ||
144 | struct omap_dss_device *omapdss_find_output_from_display(struct omap_dss_device *dssdev) | 147 | struct omap_dss_device *omapdss_find_output_from_display(struct omap_dss_device *dssdev) |
145 | { | 148 | { |
146 | return omap_dss_get_device(dssdev->output); | 149 | while (dssdev->output) |
150 | dssdev = dssdev->output; | ||
151 | |||
152 | if (dssdev->id != 0) | ||
153 | return omap_dss_get_device(dssdev); | ||
154 | |||
155 | return NULL; | ||
147 | } | 156 | } |
148 | EXPORT_SYMBOL(omapdss_find_output_from_display); | 157 | EXPORT_SYMBOL(omapdss_find_output_from_display); |
149 | 158 | ||
diff --git a/drivers/video/omap2/dss/rfbi.c b/drivers/video/omap2/dss/rfbi.c index f18c9466849b..fdfe6e6f25df 100644 --- a/drivers/video/omap2/dss/rfbi.c +++ b/drivers/video/omap2/dss/rfbi.c | |||
@@ -1022,14 +1022,14 @@ static void rfbi_init_output(struct platform_device *pdev) | |||
1022 | out->dispc_channel = OMAP_DSS_CHANNEL_LCD; | 1022 | out->dispc_channel = OMAP_DSS_CHANNEL_LCD; |
1023 | out->owner = THIS_MODULE; | 1023 | out->owner = THIS_MODULE; |
1024 | 1024 | ||
1025 | dss_register_output(out); | 1025 | omapdss_register_output(out); |
1026 | } | 1026 | } |
1027 | 1027 | ||
1028 | static void __exit rfbi_uninit_output(struct platform_device *pdev) | 1028 | static void __exit rfbi_uninit_output(struct platform_device *pdev) |
1029 | { | 1029 | { |
1030 | struct omap_dss_device *out = &rfbi.output; | 1030 | struct omap_dss_device *out = &rfbi.output; |
1031 | 1031 | ||
1032 | dss_unregister_output(out); | 1032 | omapdss_unregister_output(out); |
1033 | } | 1033 | } |
1034 | 1034 | ||
1035 | /* RFBI HW IP initialisation */ | 1035 | /* RFBI HW IP initialisation */ |
diff --git a/drivers/video/omap2/dss/sdi.c b/drivers/video/omap2/dss/sdi.c index bcb75f5d5373..856af2e89760 100644 --- a/drivers/video/omap2/dss/sdi.c +++ b/drivers/video/omap2/dss/sdi.c | |||
@@ -234,6 +234,26 @@ void omapdss_sdi_set_timings(struct omap_dss_device *dssdev, | |||
234 | } | 234 | } |
235 | EXPORT_SYMBOL(omapdss_sdi_set_timings); | 235 | EXPORT_SYMBOL(omapdss_sdi_set_timings); |
236 | 236 | ||
237 | static void sdi_get_timings(struct omap_dss_device *dssdev, | ||
238 | struct omap_video_timings *timings) | ||
239 | { | ||
240 | *timings = sdi.timings; | ||
241 | } | ||
242 | |||
243 | static int sdi_check_timings(struct omap_dss_device *dssdev, | ||
244 | struct omap_video_timings *timings) | ||
245 | { | ||
246 | struct omap_overlay_manager *mgr = sdi.output.manager; | ||
247 | |||
248 | if (mgr && !dispc_mgr_timings_ok(mgr->id, timings)) | ||
249 | return -EINVAL; | ||
250 | |||
251 | if (timings->pixel_clock == 0) | ||
252 | return -EINVAL; | ||
253 | |||
254 | return 0; | ||
255 | } | ||
256 | |||
237 | void omapdss_sdi_set_datapairs(struct omap_dss_device *dssdev, int datapairs) | 257 | void omapdss_sdi_set_datapairs(struct omap_dss_device *dssdev, int datapairs) |
238 | { | 258 | { |
239 | sdi.datapairs = datapairs; | 259 | sdi.datapairs = datapairs; |
@@ -333,6 +353,63 @@ static int sdi_probe_pdata(struct platform_device *sdidev) | |||
333 | return 0; | 353 | return 0; |
334 | } | 354 | } |
335 | 355 | ||
356 | static int sdi_connect(struct omap_dss_device *dssdev, | ||
357 | struct omap_dss_device *dst) | ||
358 | { | ||
359 | struct omap_overlay_manager *mgr; | ||
360 | int r; | ||
361 | |||
362 | r = sdi_init_regulator(); | ||
363 | if (r) | ||
364 | return r; | ||
365 | |||
366 | mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); | ||
367 | if (!mgr) | ||
368 | return -ENODEV; | ||
369 | |||
370 | r = dss_mgr_connect(mgr, dssdev); | ||
371 | if (r) | ||
372 | return r; | ||
373 | |||
374 | r = omapdss_output_set_device(dssdev, dst); | ||
375 | if (r) { | ||
376 | DSSERR("failed to connect output to new device: %s\n", | ||
377 | dst->name); | ||
378 | dss_mgr_disconnect(mgr, dssdev); | ||
379 | return r; | ||
380 | } | ||
381 | |||
382 | return 0; | ||
383 | } | ||
384 | |||
385 | static void sdi_disconnect(struct omap_dss_device *dssdev, | ||
386 | struct omap_dss_device *dst) | ||
387 | { | ||
388 | WARN_ON(dst != dssdev->device); | ||
389 | |||
390 | if (dst != dssdev->device) | ||
391 | return; | ||
392 | |||
393 | omapdss_output_unset_device(dssdev); | ||
394 | |||
395 | if (dssdev->manager) | ||
396 | dss_mgr_disconnect(dssdev->manager, dssdev); | ||
397 | } | ||
398 | |||
399 | static const struct omapdss_sdi_ops sdi_ops = { | ||
400 | .connect = sdi_connect, | ||
401 | .disconnect = sdi_disconnect, | ||
402 | |||
403 | .enable = omapdss_sdi_display_enable, | ||
404 | .disable = omapdss_sdi_display_disable, | ||
405 | |||
406 | .check_timings = sdi_check_timings, | ||
407 | .set_timings = omapdss_sdi_set_timings, | ||
408 | .get_timings = sdi_get_timings, | ||
409 | |||
410 | .set_datapairs = omapdss_sdi_set_datapairs, | ||
411 | }; | ||
412 | |||
336 | static void sdi_init_output(struct platform_device *pdev) | 413 | static void sdi_init_output(struct platform_device *pdev) |
337 | { | 414 | { |
338 | struct omap_dss_device *out = &sdi.output; | 415 | struct omap_dss_device *out = &sdi.output; |
@@ -342,16 +419,17 @@ static void sdi_init_output(struct platform_device *pdev) | |||
342 | out->output_type = OMAP_DISPLAY_TYPE_SDI; | 419 | out->output_type = OMAP_DISPLAY_TYPE_SDI; |
343 | out->name = "sdi.0"; | 420 | out->name = "sdi.0"; |
344 | out->dispc_channel = OMAP_DSS_CHANNEL_LCD; | 421 | out->dispc_channel = OMAP_DSS_CHANNEL_LCD; |
422 | out->ops.sdi = &sdi_ops; | ||
345 | out->owner = THIS_MODULE; | 423 | out->owner = THIS_MODULE; |
346 | 424 | ||
347 | dss_register_output(out); | 425 | omapdss_register_output(out); |
348 | } | 426 | } |
349 | 427 | ||
350 | static void __exit sdi_uninit_output(struct platform_device *pdev) | 428 | static void __exit sdi_uninit_output(struct platform_device *pdev) |
351 | { | 429 | { |
352 | struct omap_dss_device *out = &sdi.output; | 430 | struct omap_dss_device *out = &sdi.output; |
353 | 431 | ||
354 | dss_unregister_output(out); | 432 | omapdss_unregister_output(out); |
355 | } | 433 | } |
356 | 434 | ||
357 | static int omap_sdi_probe(struct platform_device *pdev) | 435 | static int omap_sdi_probe(struct platform_device *pdev) |
diff --git a/drivers/video/omap2/dss/venc.c b/drivers/video/omap2/dss/venc.c index 8720f137d4f8..496a106fe823 100644 --- a/drivers/video/omap2/dss/venc.c +++ b/drivers/video/omap2/dss/venc.c | |||
@@ -564,6 +564,16 @@ int omapdss_venc_check_timings(struct omap_dss_device *dssdev, | |||
564 | return -EINVAL; | 564 | return -EINVAL; |
565 | } | 565 | } |
566 | 566 | ||
567 | static void venc_get_timings(struct omap_dss_device *dssdev, | ||
568 | struct omap_video_timings *timings) | ||
569 | { | ||
570 | mutex_lock(&venc.venc_lock); | ||
571 | |||
572 | *timings = venc.timings; | ||
573 | |||
574 | mutex_unlock(&venc.venc_lock); | ||
575 | } | ||
576 | |||
567 | u32 omapdss_venc_get_wss(struct omap_dss_device *dssdev) | 577 | u32 omapdss_venc_get_wss(struct omap_dss_device *dssdev) |
568 | { | 578 | { |
569 | /* Invert due to VENC_L21_WC_CTL:INV=1 */ | 579 | /* Invert due to VENC_L21_WC_CTL:INV=1 */ |
@@ -779,6 +789,67 @@ static int venc_probe_pdata(struct platform_device *vencdev) | |||
779 | return 0; | 789 | return 0; |
780 | } | 790 | } |
781 | 791 | ||
792 | static int venc_connect(struct omap_dss_device *dssdev, | ||
793 | struct omap_dss_device *dst) | ||
794 | { | ||
795 | struct omap_overlay_manager *mgr; | ||
796 | int r; | ||
797 | |||
798 | r = venc_init_regulator(); | ||
799 | if (r) | ||
800 | return r; | ||
801 | |||
802 | mgr = omap_dss_get_overlay_manager(dssdev->dispc_channel); | ||
803 | if (!mgr) | ||
804 | return -ENODEV; | ||
805 | |||
806 | r = dss_mgr_connect(mgr, dssdev); | ||
807 | if (r) | ||
808 | return r; | ||
809 | |||
810 | r = omapdss_output_set_device(dssdev, dst); | ||
811 | if (r) { | ||
812 | DSSERR("failed to connect output to new device: %s\n", | ||
813 | dst->name); | ||
814 | dss_mgr_disconnect(mgr, dssdev); | ||
815 | return r; | ||
816 | } | ||
817 | |||
818 | return 0; | ||
819 | } | ||
820 | |||
821 | static void venc_disconnect(struct omap_dss_device *dssdev, | ||
822 | struct omap_dss_device *dst) | ||
823 | { | ||
824 | WARN_ON(dst != dssdev->device); | ||
825 | |||
826 | if (dst != dssdev->device) | ||
827 | return; | ||
828 | |||
829 | omapdss_output_unset_device(dssdev); | ||
830 | |||
831 | if (dssdev->manager) | ||
832 | dss_mgr_disconnect(dssdev->manager, dssdev); | ||
833 | } | ||
834 | |||
835 | static const struct omapdss_atv_ops venc_ops = { | ||
836 | .connect = venc_connect, | ||
837 | .disconnect = venc_disconnect, | ||
838 | |||
839 | .enable = omapdss_venc_display_enable, | ||
840 | .disable = omapdss_venc_display_disable, | ||
841 | |||
842 | .check_timings = omapdss_venc_check_timings, | ||
843 | .set_timings = omapdss_venc_set_timings, | ||
844 | .get_timings = venc_get_timings, | ||
845 | |||
846 | .set_type = omapdss_venc_set_type, | ||
847 | .invert_vid_out_polarity = omapdss_venc_invert_vid_out_polarity, | ||
848 | |||
849 | .set_wss = omapdss_venc_set_wss, | ||
850 | .get_wss = omapdss_venc_get_wss, | ||
851 | }; | ||
852 | |||
782 | static void venc_init_output(struct platform_device *pdev) | 853 | static void venc_init_output(struct platform_device *pdev) |
783 | { | 854 | { |
784 | struct omap_dss_device *out = &venc.output; | 855 | struct omap_dss_device *out = &venc.output; |
@@ -788,16 +859,17 @@ static void venc_init_output(struct platform_device *pdev) | |||
788 | out->output_type = OMAP_DISPLAY_TYPE_VENC; | 859 | out->output_type = OMAP_DISPLAY_TYPE_VENC; |
789 | out->name = "venc.0"; | 860 | out->name = "venc.0"; |
790 | out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; | 861 | out->dispc_channel = OMAP_DSS_CHANNEL_DIGIT; |
862 | out->ops.atv = &venc_ops; | ||
791 | out->owner = THIS_MODULE; | 863 | out->owner = THIS_MODULE; |
792 | 864 | ||
793 | dss_register_output(out); | 865 | omapdss_register_output(out); |
794 | } | 866 | } |
795 | 867 | ||
796 | static void __exit venc_uninit_output(struct platform_device *pdev) | 868 | static void __exit venc_uninit_output(struct platform_device *pdev) |
797 | { | 869 | { |
798 | struct omap_dss_device *out = &venc.output; | 870 | struct omap_dss_device *out = &venc.output; |
799 | 871 | ||
800 | dss_unregister_output(out); | 872 | omapdss_unregister_output(out); |
801 | } | 873 | } |
802 | 874 | ||
803 | /* VENC HW IP initialisation */ | 875 | /* VENC HW IP initialisation */ |
diff --git a/include/video/omap-panel-data.h b/include/video/omap-panel-data.h index 0c3b46d3daf3..6b2366fb6e53 100644 --- a/include/video/omap-panel-data.h +++ b/include/video/omap-panel-data.h | |||
@@ -27,6 +27,9 @@ | |||
27 | #ifndef __OMAP_PANEL_DATA_H | 27 | #ifndef __OMAP_PANEL_DATA_H |
28 | #define __OMAP_PANEL_DATA_H | 28 | #define __OMAP_PANEL_DATA_H |
29 | 29 | ||
30 | #include <video/omapdss.h> | ||
31 | #include <video/display_timing.h> | ||
32 | |||
30 | struct omap_dss_device; | 33 | struct omap_dss_device; |
31 | 34 | ||
32 | /** | 35 | /** |
@@ -147,4 +150,210 @@ struct panel_tpo_td043_data { | |||
147 | int nreset_gpio; | 150 | int nreset_gpio; |
148 | }; | 151 | }; |
149 | 152 | ||
153 | /** | ||
154 | * encoder_tfp410 platform data | ||
155 | * @name: name for this display entity | ||
156 | * @power_down_gpio: gpio number for PD pin (or -1 if not available) | ||
157 | * @data_lines: number of DPI datalines | ||
158 | */ | ||
159 | struct encoder_tfp410_platform_data { | ||
160 | const char *name; | ||
161 | const char *source; | ||
162 | int power_down_gpio; | ||
163 | int data_lines; | ||
164 | }; | ||
165 | |||
166 | /** | ||
167 | * encoder_tpd12s015 platform data | ||
168 | * @name: name for this display entity | ||
169 | * @ct_cp_hpd_gpio: CT_CP_HPD gpio number | ||
170 | * @ls_oe_gpio: LS_OE gpio number | ||
171 | * @hpd_gpio: HPD gpio number | ||
172 | */ | ||
173 | struct encoder_tpd12s015_platform_data { | ||
174 | const char *name; | ||
175 | const char *source; | ||
176 | |||
177 | int ct_cp_hpd_gpio; | ||
178 | int ls_oe_gpio; | ||
179 | int hpd_gpio; | ||
180 | }; | ||
181 | |||
182 | /** | ||
183 | * connector_dvi platform data | ||
184 | * @name: name for this display entity | ||
185 | * @source: name of the display entity used as a video source | ||
186 | * @i2c_bus_num: i2c bus number to be used for reading EDID | ||
187 | */ | ||
188 | struct connector_dvi_platform_data { | ||
189 | const char *name; | ||
190 | const char *source; | ||
191 | int i2c_bus_num; | ||
192 | }; | ||
193 | |||
194 | /** | ||
195 | * connector_hdmi platform data | ||
196 | * @name: name for this display entity | ||
197 | * @source: name of the display entity used as a video source | ||
198 | */ | ||
199 | struct connector_hdmi_platform_data { | ||
200 | const char *name; | ||
201 | const char *source; | ||
202 | }; | ||
203 | |||
204 | /** | ||
205 | * connector_atv platform data | ||
206 | * @name: name for this display entity | ||
207 | * @source: name of the display entity used as a video source | ||
208 | * @connector_type: composite/svideo | ||
209 | * @invert_polarity: invert signal polarity | ||
210 | */ | ||
211 | struct connector_atv_platform_data { | ||
212 | const char *name; | ||
213 | const char *source; | ||
214 | |||
215 | enum omap_dss_venc_type connector_type; | ||
216 | bool invert_polarity; | ||
217 | }; | ||
218 | |||
219 | /** | ||
220 | * panel_dpi platform data | ||
221 | * @name: name for this display entity | ||
222 | * @source: name of the display entity used as a video source | ||
223 | * @data_lines: number of DPI datalines | ||
224 | * @display_timing: timings for this panel | ||
225 | * @backlight_gpio: gpio to enable/disable the backlight (or -1) | ||
226 | * @enable_gpio: gpio to enable/disable the panel (or -1) | ||
227 | */ | ||
228 | struct panel_dpi_platform_data { | ||
229 | const char *name; | ||
230 | const char *source; | ||
231 | |||
232 | int data_lines; | ||
233 | |||
234 | const struct display_timing *display_timing; | ||
235 | |||
236 | int backlight_gpio; | ||
237 | int enable_gpio; | ||
238 | }; | ||
239 | |||
240 | /** | ||
241 | * panel_dsicm platform data | ||
242 | * @name: name for this display entity | ||
243 | * @source: name of the display entity used as a video source | ||
244 | * @reset_gpio: gpio to reset the panel (or -1) | ||
245 | * @use_ext_te: use external TE GPIO | ||
246 | * @ext_te_gpio: external TE GPIO | ||
247 | * @ulps_timeout: time to wait before entering ULPS, 0 = disabled (ms) | ||
248 | * @use_dsi_backlight: true if panel uses DSI command to control backlight | ||
249 | * @pin_config: DSI pin configuration | ||
250 | */ | ||
251 | struct panel_dsicm_platform_data { | ||
252 | const char *name; | ||
253 | const char *source; | ||
254 | |||
255 | int reset_gpio; | ||
256 | |||
257 | bool use_ext_te; | ||
258 | int ext_te_gpio; | ||
259 | |||
260 | unsigned ulps_timeout; | ||
261 | |||
262 | bool use_dsi_backlight; | ||
263 | |||
264 | struct omap_dsi_pin_config pin_config; | ||
265 | }; | ||
266 | |||
267 | /** | ||
268 | * panel_acx565akm platform data | ||
269 | * @name: name for this display entity | ||
270 | * @source: name of the display entity used as a video source | ||
271 | * @reset_gpio: gpio to reset the panel (or -1) | ||
272 | * @datapairs: number of SDI datapairs | ||
273 | */ | ||
274 | struct panel_acx565akm_platform_data { | ||
275 | const char *name; | ||
276 | const char *source; | ||
277 | |||
278 | int reset_gpio; | ||
279 | |||
280 | int datapairs; | ||
281 | }; | ||
282 | |||
283 | /** | ||
284 | * panel_lb035q02 platform data | ||
285 | * @name: name for this display entity | ||
286 | * @source: name of the display entity used as a video source | ||
287 | * @data_lines: number of DPI datalines | ||
288 | * @backlight_gpio: gpio to enable/disable the backlight (or -1) | ||
289 | * @enable_gpio: gpio to enable/disable the panel (or -1) | ||
290 | */ | ||
291 | struct panel_lb035q02_platform_data { | ||
292 | const char *name; | ||
293 | const char *source; | ||
294 | |||
295 | int data_lines; | ||
296 | |||
297 | int backlight_gpio; | ||
298 | int enable_gpio; | ||
299 | }; | ||
300 | |||
301 | /** | ||
302 | * panel_sharp_ls037v7dw01 platform data | ||
303 | * @name: name for this display entity | ||
304 | * @source: name of the display entity used as a video source | ||
305 | * @data_lines: number of DPI datalines | ||
306 | * @resb_gpio: reset signal GPIO | ||
307 | * @ini_gpio: power on control GPIO | ||
308 | * @mo_gpio: selection for resolution(VGA/QVGA) GPIO | ||
309 | * @lr_gpio: selection for horizontal scanning direction GPIO | ||
310 | * @ud_gpio: selection for vertical scanning direction GPIO | ||
311 | */ | ||
312 | struct panel_sharp_ls037v7dw01_platform_data { | ||
313 | const char *name; | ||
314 | const char *source; | ||
315 | |||
316 | int data_lines; | ||
317 | |||
318 | int resb_gpio; | ||
319 | int ini_gpio; | ||
320 | int mo_gpio; | ||
321 | int lr_gpio; | ||
322 | int ud_gpio; | ||
323 | }; | ||
324 | |||
325 | /** | ||
326 | * panel-tpo-td043mtea1 platform data | ||
327 | * @name: name for this display entity | ||
328 | * @source: name of the display entity used as a video source | ||
329 | * @data_lines: number of DPI datalines | ||
330 | * @nreset_gpio: reset signal | ||
331 | */ | ||
332 | struct panel_tpo_td043mtea1_platform_data { | ||
333 | const char *name; | ||
334 | const char *source; | ||
335 | |||
336 | int data_lines; | ||
337 | |||
338 | int nreset_gpio; | ||
339 | }; | ||
340 | |||
341 | /** | ||
342 | * panel-nec-nl8048hl11 platform data | ||
343 | * @name: name for this display entity | ||
344 | * @source: name of the display entity used as a video source | ||
345 | * @data_lines: number of DPI datalines | ||
346 | * @res_gpio: reset signal | ||
347 | * @qvga_gpio: selection for resolution(QVGA/WVGA) | ||
348 | */ | ||
349 | struct panel_nec_nl8048hl11_platform_data { | ||
350 | const char *name; | ||
351 | const char *source; | ||
352 | |||
353 | int data_lines; | ||
354 | |||
355 | int res_gpio; | ||
356 | int qvga_gpio; | ||
357 | }; | ||
358 | |||
150 | #endif /* __OMAP_PANEL_DATA_H */ | 359 | #endif /* __OMAP_PANEL_DATA_H */ |
diff --git a/include/video/omapdss.h b/include/video/omapdss.h index ef9db241b4a1..b39463553845 100644 --- a/include/video/omapdss.h +++ b/include/video/omapdss.h | |||
@@ -70,6 +70,7 @@ enum omap_display_type { | |||
70 | OMAP_DISPLAY_TYPE_DSI = 1 << 3, | 70 | OMAP_DISPLAY_TYPE_DSI = 1 << 3, |
71 | OMAP_DISPLAY_TYPE_VENC = 1 << 4, | 71 | OMAP_DISPLAY_TYPE_VENC = 1 << 4, |
72 | OMAP_DISPLAY_TYPE_HDMI = 1 << 5, | 72 | OMAP_DISPLAY_TYPE_HDMI = 1 << 5, |
73 | OMAP_DISPLAY_TYPE_DVI = 1 << 6, | ||
73 | }; | 74 | }; |
74 | 75 | ||
75 | enum omap_plane { | 76 | enum omap_plane { |
@@ -171,6 +172,11 @@ enum omap_dss_audio_state { | |||
171 | OMAP_DSS_AUDIO_PLAYING, | 172 | OMAP_DSS_AUDIO_PLAYING, |
172 | }; | 173 | }; |
173 | 174 | ||
175 | struct omap_dss_audio { | ||
176 | struct snd_aes_iec958 *iec; | ||
177 | struct snd_cea_861_aud_if *cea; | ||
178 | }; | ||
179 | |||
174 | enum omap_dss_rotation_type { | 180 | enum omap_dss_rotation_type { |
175 | OMAP_DSS_ROT_DMA = 1 << 0, | 181 | OMAP_DSS_ROT_DMA = 1 << 0, |
176 | OMAP_DSS_ROT_VRFB = 1 << 1, | 182 | OMAP_DSS_ROT_VRFB = 1 << 1, |
@@ -572,6 +578,176 @@ struct omap_dss_writeback_info { | |||
572 | u8 pre_mult_alpha; | 578 | u8 pre_mult_alpha; |
573 | }; | 579 | }; |
574 | 580 | ||
581 | struct omapdss_dpi_ops { | ||
582 | int (*connect)(struct omap_dss_device *dssdev, | ||
583 | struct omap_dss_device *dst); | ||
584 | void (*disconnect)(struct omap_dss_device *dssdev, | ||
585 | struct omap_dss_device *dst); | ||
586 | |||
587 | int (*enable)(struct omap_dss_device *dssdev); | ||
588 | void (*disable)(struct omap_dss_device *dssdev); | ||
589 | |||
590 | int (*check_timings)(struct omap_dss_device *dssdev, | ||
591 | struct omap_video_timings *timings); | ||
592 | void (*set_timings)(struct omap_dss_device *dssdev, | ||
593 | struct omap_video_timings *timings); | ||
594 | void (*get_timings)(struct omap_dss_device *dssdev, | ||
595 | struct omap_video_timings *timings); | ||
596 | |||
597 | void (*set_data_lines)(struct omap_dss_device *dssdev, int data_lines); | ||
598 | }; | ||
599 | |||
600 | struct omapdss_sdi_ops { | ||
601 | int (*connect)(struct omap_dss_device *dssdev, | ||
602 | struct omap_dss_device *dst); | ||
603 | void (*disconnect)(struct omap_dss_device *dssdev, | ||
604 | struct omap_dss_device *dst); | ||
605 | |||
606 | int (*enable)(struct omap_dss_device *dssdev); | ||
607 | void (*disable)(struct omap_dss_device *dssdev); | ||
608 | |||
609 | int (*check_timings)(struct omap_dss_device *dssdev, | ||
610 | struct omap_video_timings *timings); | ||
611 | void (*set_timings)(struct omap_dss_device *dssdev, | ||
612 | struct omap_video_timings *timings); | ||
613 | void (*get_timings)(struct omap_dss_device *dssdev, | ||
614 | struct omap_video_timings *timings); | ||
615 | |||
616 | void (*set_datapairs)(struct omap_dss_device *dssdev, int datapairs); | ||
617 | }; | ||
618 | |||
619 | struct omapdss_dvi_ops { | ||
620 | int (*connect)(struct omap_dss_device *dssdev, | ||
621 | struct omap_dss_device *dst); | ||
622 | void (*disconnect)(struct omap_dss_device *dssdev, | ||
623 | struct omap_dss_device *dst); | ||
624 | |||
625 | int (*enable)(struct omap_dss_device *dssdev); | ||
626 | void (*disable)(struct omap_dss_device *dssdev); | ||
627 | |||
628 | int (*check_timings)(struct omap_dss_device *dssdev, | ||
629 | struct omap_video_timings *timings); | ||
630 | void (*set_timings)(struct omap_dss_device *dssdev, | ||
631 | struct omap_video_timings *timings); | ||
632 | void (*get_timings)(struct omap_dss_device *dssdev, | ||
633 | struct omap_video_timings *timings); | ||
634 | }; | ||
635 | |||
636 | struct omapdss_atv_ops { | ||
637 | int (*connect)(struct omap_dss_device *dssdev, | ||
638 | struct omap_dss_device *dst); | ||
639 | void (*disconnect)(struct omap_dss_device *dssdev, | ||
640 | struct omap_dss_device *dst); | ||
641 | |||
642 | int (*enable)(struct omap_dss_device *dssdev); | ||
643 | void (*disable)(struct omap_dss_device *dssdev); | ||
644 | |||
645 | int (*check_timings)(struct omap_dss_device *dssdev, | ||
646 | struct omap_video_timings *timings); | ||
647 | void (*set_timings)(struct omap_dss_device *dssdev, | ||
648 | struct omap_video_timings *timings); | ||
649 | void (*get_timings)(struct omap_dss_device *dssdev, | ||
650 | struct omap_video_timings *timings); | ||
651 | |||
652 | void (*set_type)(struct omap_dss_device *dssdev, | ||
653 | enum omap_dss_venc_type type); | ||
654 | void (*invert_vid_out_polarity)(struct omap_dss_device *dssdev, | ||
655 | bool invert_polarity); | ||
656 | |||
657 | int (*set_wss)(struct omap_dss_device *dssdev, u32 wss); | ||
658 | u32 (*get_wss)(struct omap_dss_device *dssdev); | ||
659 | }; | ||
660 | |||
661 | struct omapdss_hdmi_ops { | ||
662 | int (*connect)(struct omap_dss_device *dssdev, | ||
663 | struct omap_dss_device *dst); | ||
664 | void (*disconnect)(struct omap_dss_device *dssdev, | ||
665 | struct omap_dss_device *dst); | ||
666 | |||
667 | int (*enable)(struct omap_dss_device *dssdev); | ||
668 | void (*disable)(struct omap_dss_device *dssdev); | ||
669 | |||
670 | int (*check_timings)(struct omap_dss_device *dssdev, | ||
671 | struct omap_video_timings *timings); | ||
672 | void (*set_timings)(struct omap_dss_device *dssdev, | ||
673 | struct omap_video_timings *timings); | ||
674 | void (*get_timings)(struct omap_dss_device *dssdev, | ||
675 | struct omap_video_timings *timings); | ||
676 | |||
677 | int (*read_edid)(struct omap_dss_device *dssdev, u8 *buf, int len); | ||
678 | bool (*detect)(struct omap_dss_device *dssdev); | ||
679 | |||
680 | /* | ||
681 | * Note: These functions might sleep. Do not call while | ||
682 | * holding a spinlock/readlock. | ||
683 | */ | ||
684 | int (*audio_enable)(struct omap_dss_device *dssdev); | ||
685 | void (*audio_disable)(struct omap_dss_device *dssdev); | ||
686 | bool (*audio_supported)(struct omap_dss_device *dssdev); | ||
687 | int (*audio_config)(struct omap_dss_device *dssdev, | ||
688 | struct omap_dss_audio *audio); | ||
689 | /* Note: These functions may not sleep */ | ||
690 | int (*audio_start)(struct omap_dss_device *dssdev); | ||
691 | void (*audio_stop)(struct omap_dss_device *dssdev); | ||
692 | }; | ||
693 | |||
694 | struct omapdss_dsi_ops { | ||
695 | int (*connect)(struct omap_dss_device *dssdev, | ||
696 | struct omap_dss_device *dst); | ||
697 | void (*disconnect)(struct omap_dss_device *dssdev, | ||
698 | struct omap_dss_device *dst); | ||
699 | |||
700 | int (*enable)(struct omap_dss_device *dssdev); | ||
701 | void (*disable)(struct omap_dss_device *dssdev, bool disconnect_lanes, | ||
702 | bool enter_ulps); | ||
703 | |||
704 | /* bus configuration */ | ||
705 | int (*set_config)(struct omap_dss_device *dssdev, | ||
706 | const struct omap_dss_dsi_config *cfg); | ||
707 | int (*configure_pins)(struct omap_dss_device *dssdev, | ||
708 | const struct omap_dsi_pin_config *pin_cfg); | ||
709 | |||
710 | void (*enable_hs)(struct omap_dss_device *dssdev, int channel, | ||
711 | bool enable); | ||
712 | int (*enable_te)(struct omap_dss_device *dssdev, bool enable); | ||
713 | |||
714 | int (*update)(struct omap_dss_device *dssdev, int channel, | ||
715 | void (*callback)(int, void *), void *data); | ||
716 | |||
717 | void (*bus_lock)(struct omap_dss_device *dssdev); | ||
718 | void (*bus_unlock)(struct omap_dss_device *dssdev); | ||
719 | |||
720 | int (*enable_video_output)(struct omap_dss_device *dssdev, int channel); | ||
721 | void (*disable_video_output)(struct omap_dss_device *dssdev, | ||
722 | int channel); | ||
723 | |||
724 | int (*request_vc)(struct omap_dss_device *dssdev, int *channel); | ||
725 | int (*set_vc_id)(struct omap_dss_device *dssdev, int channel, | ||
726 | int vc_id); | ||
727 | void (*release_vc)(struct omap_dss_device *dssdev, int channel); | ||
728 | |||
729 | /* data transfer */ | ||
730 | int (*dcs_write)(struct omap_dss_device *dssdev, int channel, | ||
731 | u8 *data, int len); | ||
732 | int (*dcs_write_nosync)(struct omap_dss_device *dssdev, int channel, | ||
733 | u8 *data, int len); | ||
734 | int (*dcs_read)(struct omap_dss_device *dssdev, int channel, u8 dcs_cmd, | ||
735 | u8 *data, int len); | ||
736 | |||
737 | int (*gen_write)(struct omap_dss_device *dssdev, int channel, | ||
738 | u8 *data, int len); | ||
739 | int (*gen_write_nosync)(struct omap_dss_device *dssdev, int channel, | ||
740 | u8 *data, int len); | ||
741 | int (*gen_read)(struct omap_dss_device *dssdev, int channel, | ||
742 | u8 *reqdata, int reqlen, | ||
743 | u8 *data, int len); | ||
744 | |||
745 | int (*bta_sync)(struct omap_dss_device *dssdev, int channel); | ||
746 | |||
747 | int (*set_max_rx_packet_size)(struct omap_dss_device *dssdev, | ||
748 | int channel, u16 plen); | ||
749 | }; | ||
750 | |||
575 | struct omap_dss_device { | 751 | struct omap_dss_device { |
576 | /* old device, to be removed */ | 752 | /* old device, to be removed */ |
577 | struct device old_dev; | 753 | struct device old_dev; |
@@ -637,6 +813,15 @@ struct omap_dss_device { | |||
637 | 813 | ||
638 | struct omap_dss_driver *driver; | 814 | struct omap_dss_driver *driver; |
639 | 815 | ||
816 | union { | ||
817 | const struct omapdss_dpi_ops *dpi; | ||
818 | const struct omapdss_sdi_ops *sdi; | ||
819 | const struct omapdss_dvi_ops *dvi; | ||
820 | const struct omapdss_hdmi_ops *hdmi; | ||
821 | const struct omapdss_atv_ops *atv; | ||
822 | const struct omapdss_dsi_ops *dsi; | ||
823 | } ops; | ||
824 | |||
640 | /* helper variable for driver suspend/resume */ | 825 | /* helper variable for driver suspend/resume */ |
641 | bool activate_after_resume; | 826 | bool activate_after_resume; |
642 | 827 | ||
@@ -671,11 +856,6 @@ struct omap_dss_hdmi_data | |||
671 | int hpd_gpio; | 856 | int hpd_gpio; |
672 | }; | 857 | }; |
673 | 858 | ||
674 | struct omap_dss_audio { | ||
675 | struct snd_aes_iec958 *iec; | ||
676 | struct snd_cea_861_aud_if *cea; | ||
677 | }; | ||
678 | |||
679 | struct omap_dss_driver { | 859 | struct omap_dss_driver { |
680 | struct device_driver driver; | 860 | struct device_driver driver; |
681 | 861 | ||
@@ -780,6 +960,8 @@ struct omap_overlay_manager *omap_dss_get_overlay_manager(int num); | |||
780 | int omap_dss_get_num_overlays(void); | 960 | int omap_dss_get_num_overlays(void); |
781 | struct omap_overlay *omap_dss_get_overlay(int num); | 961 | struct omap_overlay *omap_dss_get_overlay(int num); |
782 | 962 | ||
963 | int omapdss_register_output(struct omap_dss_device *output); | ||
964 | void omapdss_unregister_output(struct omap_dss_device *output); | ||
783 | struct omap_dss_device *omap_dss_get_output(enum omap_dss_output_id id); | 965 | struct omap_dss_device *omap_dss_get_output(enum omap_dss_output_id id); |
784 | struct omap_dss_device *omap_dss_find_output(const char *name); | 966 | struct omap_dss_device *omap_dss_find_output(const char *name); |
785 | struct omap_dss_device *omap_dss_find_output_by_node(struct device_node *node); | 967 | struct omap_dss_device *omap_dss_find_output_by_node(struct device_node *node); |