1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2f76ee892STomi Valkeinen /*
3f76ee892STomi Valkeinen * Generic DSI Command Mode panel driver
4f76ee892STomi Valkeinen *
5f76ee892STomi Valkeinen * Copyright (C) 2013 Texas Instruments
6f76ee892STomi Valkeinen * Author: Tomi Valkeinen <tomi.valkeinen@ti.com>
7f76ee892STomi Valkeinen */
8f76ee892STomi Valkeinen
9f76ee892STomi Valkeinen /* #define DEBUG */
10f76ee892STomi Valkeinen
11f76ee892STomi Valkeinen #include <linux/backlight.h>
12f76ee892STomi Valkeinen #include <linux/delay.h>
1367c366deSDmitry Torokhov #include <linux/err.h>
14f76ee892STomi Valkeinen #include <linux/fb.h>
1567c366deSDmitry Torokhov #include <linux/gpio/consumer.h>
16f76ee892STomi Valkeinen #include <linux/interrupt.h>
17f76ee892STomi Valkeinen #include <linux/jiffies.h>
18e8812acbSRob Herring #include <linux/mod_devicetable.h>
19f76ee892STomi Valkeinen #include <linux/module.h>
20f76ee892STomi Valkeinen #include <linux/platform_device.h>
21174cd4b1SIngo Molnar #include <linux/sched/signal.h>
22f76ee892STomi Valkeinen #include <linux/slab.h>
23f76ee892STomi Valkeinen #include <linux/workqueue.h>
24f76ee892STomi Valkeinen
2562d9e44eSPeter Ujfalusi #include <video/omapfb_dss.h>
26f76ee892STomi Valkeinen #include <video/mipi_display.h>
27f76ee892STomi Valkeinen
28f76ee892STomi Valkeinen /* DSI Virtual channel. Hardcoded for now. */
29f76ee892STomi Valkeinen #define TCH 0
30f76ee892STomi Valkeinen
31f76ee892STomi Valkeinen #define DCS_READ_NUM_ERRORS 0x05
32f76ee892STomi Valkeinen #define DCS_BRIGHTNESS 0x51
33f76ee892STomi Valkeinen #define DCS_CTRL_DISPLAY 0x53
34f76ee892STomi Valkeinen #define DCS_GET_ID1 0xda
35f76ee892STomi Valkeinen #define DCS_GET_ID2 0xdb
36f76ee892STomi Valkeinen #define DCS_GET_ID3 0xdc
37f76ee892STomi Valkeinen
38f76ee892STomi Valkeinen struct panel_drv_data {
39f76ee892STomi Valkeinen struct omap_dss_device dssdev;
40f76ee892STomi Valkeinen struct omap_dss_device *in;
41f76ee892STomi Valkeinen
42f76ee892STomi Valkeinen struct omap_video_timings timings;
43f76ee892STomi Valkeinen
44f76ee892STomi Valkeinen struct platform_device *pdev;
45f76ee892STomi Valkeinen
46f76ee892STomi Valkeinen struct mutex lock;
47f76ee892STomi Valkeinen
48f76ee892STomi Valkeinen struct backlight_device *bldev;
49f76ee892STomi Valkeinen
50f76ee892STomi Valkeinen unsigned long hw_guard_end; /* next value of jiffies when we can
51f76ee892STomi Valkeinen * issue the next sleep in/out command
52f76ee892STomi Valkeinen */
53f76ee892STomi Valkeinen unsigned long hw_guard_wait; /* max guard time in jiffies */
54f76ee892STomi Valkeinen
55f76ee892STomi Valkeinen /* panel HW configuration from DT or platform data */
5667c366deSDmitry Torokhov struct gpio_desc *reset_gpio;
5767c366deSDmitry Torokhov struct gpio_desc *ext_te_gpio;
58f76ee892STomi Valkeinen
59f76ee892STomi Valkeinen bool use_dsi_backlight;
60f76ee892STomi Valkeinen
61f76ee892STomi Valkeinen struct omap_dsi_pin_config pin_config;
62f76ee892STomi Valkeinen
63f76ee892STomi Valkeinen /* runtime variables */
64f76ee892STomi Valkeinen bool enabled;
65f76ee892STomi Valkeinen
66f76ee892STomi Valkeinen bool te_enabled;
67f76ee892STomi Valkeinen
68f76ee892STomi Valkeinen atomic_t do_update;
69f76ee892STomi Valkeinen int channel;
70f76ee892STomi Valkeinen
71f76ee892STomi Valkeinen struct delayed_work te_timeout_work;
72f76ee892STomi Valkeinen
73f76ee892STomi Valkeinen bool intro_printed;
74f76ee892STomi Valkeinen
75f76ee892STomi Valkeinen bool ulps_enabled;
76f76ee892STomi Valkeinen unsigned ulps_timeout;
77f76ee892STomi Valkeinen struct delayed_work ulps_work;
78f76ee892STomi Valkeinen };
79f76ee892STomi Valkeinen
80f76ee892STomi Valkeinen #define to_panel_data(p) container_of(p, struct panel_drv_data, dssdev)
81f76ee892STomi Valkeinen
82f76ee892STomi Valkeinen static irqreturn_t dsicm_te_isr(int irq, void *data);
83f76ee892STomi Valkeinen static void dsicm_te_timeout_work_callback(struct work_struct *work);
84f76ee892STomi Valkeinen static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable);
85f76ee892STomi Valkeinen
86f76ee892STomi Valkeinen static int dsicm_panel_reset(struct panel_drv_data *ddata);
87f76ee892STomi Valkeinen
88f76ee892STomi Valkeinen static void dsicm_ulps_work(struct work_struct *work);
89f76ee892STomi Valkeinen
hw_guard_start(struct panel_drv_data * ddata,int guard_msec)90f76ee892STomi Valkeinen static void hw_guard_start(struct panel_drv_data *ddata, int guard_msec)
91f76ee892STomi Valkeinen {
92f76ee892STomi Valkeinen ddata->hw_guard_wait = msecs_to_jiffies(guard_msec);
93f76ee892STomi Valkeinen ddata->hw_guard_end = jiffies + ddata->hw_guard_wait;
94f76ee892STomi Valkeinen }
95f76ee892STomi Valkeinen
hw_guard_wait(struct panel_drv_data * ddata)96f76ee892STomi Valkeinen static void hw_guard_wait(struct panel_drv_data *ddata)
97f76ee892STomi Valkeinen {
98f76ee892STomi Valkeinen unsigned long wait = ddata->hw_guard_end - jiffies;
99f76ee892STomi Valkeinen
100ac5b52d2SKarim Eshapa if ((long)wait > 0 && time_before_eq(wait, ddata->hw_guard_wait)) {
101f76ee892STomi Valkeinen set_current_state(TASK_UNINTERRUPTIBLE);
102f76ee892STomi Valkeinen schedule_timeout(wait);
103f76ee892STomi Valkeinen }
104f76ee892STomi Valkeinen }
105f76ee892STomi Valkeinen
dsicm_dcs_read_1(struct panel_drv_data * ddata,u8 dcs_cmd,u8 * data)106f76ee892STomi Valkeinen static int dsicm_dcs_read_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 *data)
107f76ee892STomi Valkeinen {
108f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
109f76ee892STomi Valkeinen int r;
110f76ee892STomi Valkeinen u8 buf[1];
111f76ee892STomi Valkeinen
112f76ee892STomi Valkeinen r = in->ops.dsi->dcs_read(in, ddata->channel, dcs_cmd, buf, 1);
113f76ee892STomi Valkeinen
114f76ee892STomi Valkeinen if (r < 0)
115f76ee892STomi Valkeinen return r;
116f76ee892STomi Valkeinen
117f76ee892STomi Valkeinen *data = buf[0];
118f76ee892STomi Valkeinen
119f76ee892STomi Valkeinen return 0;
120f76ee892STomi Valkeinen }
121f76ee892STomi Valkeinen
dsicm_dcs_write_0(struct panel_drv_data * ddata,u8 dcs_cmd)122f76ee892STomi Valkeinen static int dsicm_dcs_write_0(struct panel_drv_data *ddata, u8 dcs_cmd)
123f76ee892STomi Valkeinen {
124f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
125f76ee892STomi Valkeinen return in->ops.dsi->dcs_write(in, ddata->channel, &dcs_cmd, 1);
126f76ee892STomi Valkeinen }
127f76ee892STomi Valkeinen
dsicm_dcs_write_1(struct panel_drv_data * ddata,u8 dcs_cmd,u8 param)128f76ee892STomi Valkeinen static int dsicm_dcs_write_1(struct panel_drv_data *ddata, u8 dcs_cmd, u8 param)
129f76ee892STomi Valkeinen {
130f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
131f76ee892STomi Valkeinen u8 buf[2] = { dcs_cmd, param };
132f76ee892STomi Valkeinen
133f76ee892STomi Valkeinen return in->ops.dsi->dcs_write(in, ddata->channel, buf, 2);
134f76ee892STomi Valkeinen }
135f76ee892STomi Valkeinen
dsicm_sleep_in(struct panel_drv_data * ddata)136f76ee892STomi Valkeinen static int dsicm_sleep_in(struct panel_drv_data *ddata)
137f76ee892STomi Valkeinen
138f76ee892STomi Valkeinen {
139f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
140f76ee892STomi Valkeinen u8 cmd;
141f76ee892STomi Valkeinen int r;
142f76ee892STomi Valkeinen
143f76ee892STomi Valkeinen hw_guard_wait(ddata);
144f76ee892STomi Valkeinen
145f76ee892STomi Valkeinen cmd = MIPI_DCS_ENTER_SLEEP_MODE;
146f76ee892STomi Valkeinen r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, &cmd, 1);
147f76ee892STomi Valkeinen if (r)
148f76ee892STomi Valkeinen return r;
149f76ee892STomi Valkeinen
150f76ee892STomi Valkeinen hw_guard_start(ddata, 120);
151f76ee892STomi Valkeinen
152f76ee892STomi Valkeinen usleep_range(5000, 10000);
153f76ee892STomi Valkeinen
154f76ee892STomi Valkeinen return 0;
155f76ee892STomi Valkeinen }
156f76ee892STomi Valkeinen
dsicm_sleep_out(struct panel_drv_data * ddata)157f76ee892STomi Valkeinen static int dsicm_sleep_out(struct panel_drv_data *ddata)
158f76ee892STomi Valkeinen {
159f76ee892STomi Valkeinen int r;
160f76ee892STomi Valkeinen
161f76ee892STomi Valkeinen hw_guard_wait(ddata);
162f76ee892STomi Valkeinen
163f76ee892STomi Valkeinen r = dsicm_dcs_write_0(ddata, MIPI_DCS_EXIT_SLEEP_MODE);
164f76ee892STomi Valkeinen if (r)
165f76ee892STomi Valkeinen return r;
166f76ee892STomi Valkeinen
167f76ee892STomi Valkeinen hw_guard_start(ddata, 120);
168f76ee892STomi Valkeinen
169f76ee892STomi Valkeinen usleep_range(5000, 10000);
170f76ee892STomi Valkeinen
171f76ee892STomi Valkeinen return 0;
172f76ee892STomi Valkeinen }
173f76ee892STomi Valkeinen
dsicm_get_id(struct panel_drv_data * ddata,u8 * id1,u8 * id2,u8 * id3)174f76ee892STomi Valkeinen static int dsicm_get_id(struct panel_drv_data *ddata, u8 *id1, u8 *id2, u8 *id3)
175f76ee892STomi Valkeinen {
176f76ee892STomi Valkeinen int r;
177f76ee892STomi Valkeinen
178f76ee892STomi Valkeinen r = dsicm_dcs_read_1(ddata, DCS_GET_ID1, id1);
179f76ee892STomi Valkeinen if (r)
180f76ee892STomi Valkeinen return r;
181f76ee892STomi Valkeinen r = dsicm_dcs_read_1(ddata, DCS_GET_ID2, id2);
182f76ee892STomi Valkeinen if (r)
183f76ee892STomi Valkeinen return r;
184f76ee892STomi Valkeinen r = dsicm_dcs_read_1(ddata, DCS_GET_ID3, id3);
185f76ee892STomi Valkeinen if (r)
186f76ee892STomi Valkeinen return r;
187f76ee892STomi Valkeinen
188f76ee892STomi Valkeinen return 0;
189f76ee892STomi Valkeinen }
190f76ee892STomi Valkeinen
dsicm_set_update_window(struct panel_drv_data * ddata,u16 x,u16 y,u16 w,u16 h)191f76ee892STomi Valkeinen static int dsicm_set_update_window(struct panel_drv_data *ddata,
192f76ee892STomi Valkeinen u16 x, u16 y, u16 w, u16 h)
193f76ee892STomi Valkeinen {
194f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
195f76ee892STomi Valkeinen int r;
196f76ee892STomi Valkeinen u16 x1 = x;
197f76ee892STomi Valkeinen u16 x2 = x + w - 1;
198f76ee892STomi Valkeinen u16 y1 = y;
199f76ee892STomi Valkeinen u16 y2 = y + h - 1;
200f76ee892STomi Valkeinen
201f76ee892STomi Valkeinen u8 buf[5];
202f76ee892STomi Valkeinen buf[0] = MIPI_DCS_SET_COLUMN_ADDRESS;
203f76ee892STomi Valkeinen buf[1] = (x1 >> 8) & 0xff;
204f76ee892STomi Valkeinen buf[2] = (x1 >> 0) & 0xff;
205f76ee892STomi Valkeinen buf[3] = (x2 >> 8) & 0xff;
206f76ee892STomi Valkeinen buf[4] = (x2 >> 0) & 0xff;
207f76ee892STomi Valkeinen
208f76ee892STomi Valkeinen r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, buf, sizeof(buf));
209f76ee892STomi Valkeinen if (r)
210f76ee892STomi Valkeinen return r;
211f76ee892STomi Valkeinen
212f76ee892STomi Valkeinen buf[0] = MIPI_DCS_SET_PAGE_ADDRESS;
213f76ee892STomi Valkeinen buf[1] = (y1 >> 8) & 0xff;
214f76ee892STomi Valkeinen buf[2] = (y1 >> 0) & 0xff;
215f76ee892STomi Valkeinen buf[3] = (y2 >> 8) & 0xff;
216f76ee892STomi Valkeinen buf[4] = (y2 >> 0) & 0xff;
217f76ee892STomi Valkeinen
218f76ee892STomi Valkeinen r = in->ops.dsi->dcs_write_nosync(in, ddata->channel, buf, sizeof(buf));
219f76ee892STomi Valkeinen if (r)
220f76ee892STomi Valkeinen return r;
221f76ee892STomi Valkeinen
222f76ee892STomi Valkeinen in->ops.dsi->bta_sync(in, ddata->channel);
223f76ee892STomi Valkeinen
224f76ee892STomi Valkeinen return r;
225f76ee892STomi Valkeinen }
226f76ee892STomi Valkeinen
dsicm_queue_ulps_work(struct panel_drv_data * ddata)227f76ee892STomi Valkeinen static void dsicm_queue_ulps_work(struct panel_drv_data *ddata)
228f76ee892STomi Valkeinen {
229f76ee892STomi Valkeinen if (ddata->ulps_timeout > 0)
230b59ac9a3SBhaktipriya Shridhar schedule_delayed_work(&ddata->ulps_work,
231f76ee892STomi Valkeinen msecs_to_jiffies(ddata->ulps_timeout));
232f76ee892STomi Valkeinen }
233f76ee892STomi Valkeinen
dsicm_cancel_ulps_work(struct panel_drv_data * ddata)234f76ee892STomi Valkeinen static void dsicm_cancel_ulps_work(struct panel_drv_data *ddata)
235f76ee892STomi Valkeinen {
236f76ee892STomi Valkeinen cancel_delayed_work(&ddata->ulps_work);
237f76ee892STomi Valkeinen }
238f76ee892STomi Valkeinen
dsicm_enter_ulps(struct panel_drv_data * ddata)239f76ee892STomi Valkeinen static int dsicm_enter_ulps(struct panel_drv_data *ddata)
240f76ee892STomi Valkeinen {
241f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
242f76ee892STomi Valkeinen int r;
243f76ee892STomi Valkeinen
244f76ee892STomi Valkeinen if (ddata->ulps_enabled)
245f76ee892STomi Valkeinen return 0;
246f76ee892STomi Valkeinen
247f76ee892STomi Valkeinen dsicm_cancel_ulps_work(ddata);
248f76ee892STomi Valkeinen
249f76ee892STomi Valkeinen r = _dsicm_enable_te(ddata, false);
250f76ee892STomi Valkeinen if (r)
251f76ee892STomi Valkeinen goto err;
252f76ee892STomi Valkeinen
25367c366deSDmitry Torokhov if (ddata->ext_te_gpio)
25467c366deSDmitry Torokhov disable_irq(gpiod_to_irq(ddata->ext_te_gpio));
255f76ee892STomi Valkeinen
256f76ee892STomi Valkeinen in->ops.dsi->disable(in, false, true);
257f76ee892STomi Valkeinen
258f76ee892STomi Valkeinen ddata->ulps_enabled = true;
259f76ee892STomi Valkeinen
260f76ee892STomi Valkeinen return 0;
261f76ee892STomi Valkeinen
262f76ee892STomi Valkeinen err:
263f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "enter ULPS failed");
264f76ee892STomi Valkeinen dsicm_panel_reset(ddata);
265f76ee892STomi Valkeinen
266f76ee892STomi Valkeinen ddata->ulps_enabled = false;
267f76ee892STomi Valkeinen
268f76ee892STomi Valkeinen dsicm_queue_ulps_work(ddata);
269f76ee892STomi Valkeinen
270f76ee892STomi Valkeinen return r;
271f76ee892STomi Valkeinen }
272f76ee892STomi Valkeinen
dsicm_exit_ulps(struct panel_drv_data * ddata)273f76ee892STomi Valkeinen static int dsicm_exit_ulps(struct panel_drv_data *ddata)
274f76ee892STomi Valkeinen {
275f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
276f76ee892STomi Valkeinen int r;
277f76ee892STomi Valkeinen
278f76ee892STomi Valkeinen if (!ddata->ulps_enabled)
279f76ee892STomi Valkeinen return 0;
280f76ee892STomi Valkeinen
281f76ee892STomi Valkeinen r = in->ops.dsi->enable(in);
282f76ee892STomi Valkeinen if (r) {
283f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "failed to enable DSI\n");
284f76ee892STomi Valkeinen goto err1;
285f76ee892STomi Valkeinen }
286f76ee892STomi Valkeinen
287f76ee892STomi Valkeinen in->ops.dsi->enable_hs(in, ddata->channel, true);
288f76ee892STomi Valkeinen
289f76ee892STomi Valkeinen r = _dsicm_enable_te(ddata, true);
290f76ee892STomi Valkeinen if (r) {
291f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "failed to re-enable TE");
292f76ee892STomi Valkeinen goto err2;
293f76ee892STomi Valkeinen }
294f76ee892STomi Valkeinen
29567c366deSDmitry Torokhov if (ddata->ext_te_gpio)
29667c366deSDmitry Torokhov enable_irq(gpiod_to_irq(ddata->ext_te_gpio));
297f76ee892STomi Valkeinen
298f76ee892STomi Valkeinen dsicm_queue_ulps_work(ddata);
299f76ee892STomi Valkeinen
300f76ee892STomi Valkeinen ddata->ulps_enabled = false;
301f76ee892STomi Valkeinen
302f76ee892STomi Valkeinen return 0;
303f76ee892STomi Valkeinen
304f76ee892STomi Valkeinen err2:
305f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "failed to exit ULPS");
306f76ee892STomi Valkeinen
307f76ee892STomi Valkeinen r = dsicm_panel_reset(ddata);
308f76ee892STomi Valkeinen if (!r) {
30967c366deSDmitry Torokhov if (ddata->ext_te_gpio)
31067c366deSDmitry Torokhov enable_irq(gpiod_to_irq(ddata->ext_te_gpio));
311f76ee892STomi Valkeinen ddata->ulps_enabled = false;
312f76ee892STomi Valkeinen }
313f76ee892STomi Valkeinen err1:
314f76ee892STomi Valkeinen dsicm_queue_ulps_work(ddata);
315f76ee892STomi Valkeinen
316f76ee892STomi Valkeinen return r;
317f76ee892STomi Valkeinen }
318f76ee892STomi Valkeinen
dsicm_wake_up(struct panel_drv_data * ddata)319f76ee892STomi Valkeinen static int dsicm_wake_up(struct panel_drv_data *ddata)
320f76ee892STomi Valkeinen {
321f76ee892STomi Valkeinen if (ddata->ulps_enabled)
322f76ee892STomi Valkeinen return dsicm_exit_ulps(ddata);
323f76ee892STomi Valkeinen
324f76ee892STomi Valkeinen dsicm_cancel_ulps_work(ddata);
325f76ee892STomi Valkeinen dsicm_queue_ulps_work(ddata);
326f76ee892STomi Valkeinen return 0;
327f76ee892STomi Valkeinen }
328f76ee892STomi Valkeinen
dsicm_bl_update_status(struct backlight_device * dev)329f76ee892STomi Valkeinen static int dsicm_bl_update_status(struct backlight_device *dev)
330f76ee892STomi Valkeinen {
331f76ee892STomi Valkeinen struct panel_drv_data *ddata = dev_get_drvdata(&dev->dev);
332f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
333f76ee892STomi Valkeinen int r;
334450afd92SStephen Kitt int level = backlight_get_brightness(dev);
335f76ee892STomi Valkeinen
336f76ee892STomi Valkeinen dev_dbg(&ddata->pdev->dev, "update brightness to %d\n", level);
337f76ee892STomi Valkeinen
338f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
339f76ee892STomi Valkeinen
340f76ee892STomi Valkeinen if (ddata->enabled) {
341f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
342f76ee892STomi Valkeinen
343f76ee892STomi Valkeinen r = dsicm_wake_up(ddata);
344f76ee892STomi Valkeinen if (!r)
345f76ee892STomi Valkeinen r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, level);
346f76ee892STomi Valkeinen
347f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
348f76ee892STomi Valkeinen } else {
349f76ee892STomi Valkeinen r = 0;
350f76ee892STomi Valkeinen }
351f76ee892STomi Valkeinen
352f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
353f76ee892STomi Valkeinen
354f76ee892STomi Valkeinen return r;
355f76ee892STomi Valkeinen }
356f76ee892STomi Valkeinen
dsicm_bl_get_intensity(struct backlight_device * dev)357f76ee892STomi Valkeinen static int dsicm_bl_get_intensity(struct backlight_device *dev)
358f76ee892STomi Valkeinen {
3596be0fb64SThomas Zimmermann return backlight_get_brightness(dev);
360f76ee892STomi Valkeinen }
361f76ee892STomi Valkeinen
362f76ee892STomi Valkeinen static const struct backlight_ops dsicm_bl_ops = {
363f76ee892STomi Valkeinen .get_brightness = dsicm_bl_get_intensity,
364f76ee892STomi Valkeinen .update_status = dsicm_bl_update_status,
365f76ee892STomi Valkeinen };
366f76ee892STomi Valkeinen
dsicm_get_resolution(struct omap_dss_device * dssdev,u16 * xres,u16 * yres)367f76ee892STomi Valkeinen static void dsicm_get_resolution(struct omap_dss_device *dssdev,
368f76ee892STomi Valkeinen u16 *xres, u16 *yres)
369f76ee892STomi Valkeinen {
370f76ee892STomi Valkeinen *xres = dssdev->panel.timings.x_res;
371f76ee892STomi Valkeinen *yres = dssdev->panel.timings.y_res;
372f76ee892STomi Valkeinen }
373f76ee892STomi Valkeinen
dsicm_num_errors_show(struct device * dev,struct device_attribute * attr,char * buf)374f76ee892STomi Valkeinen static ssize_t dsicm_num_errors_show(struct device *dev,
375f76ee892STomi Valkeinen struct device_attribute *attr, char *buf)
376f76ee892STomi Valkeinen {
3776677b275SWolfram Sang struct panel_drv_data *ddata = dev_get_drvdata(dev);
378f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
379f76ee892STomi Valkeinen u8 errors = 0;
380f76ee892STomi Valkeinen int r;
381f76ee892STomi Valkeinen
382f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
383f76ee892STomi Valkeinen
384f76ee892STomi Valkeinen if (ddata->enabled) {
385f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
386f76ee892STomi Valkeinen
387f76ee892STomi Valkeinen r = dsicm_wake_up(ddata);
388f76ee892STomi Valkeinen if (!r)
389f76ee892STomi Valkeinen r = dsicm_dcs_read_1(ddata, DCS_READ_NUM_ERRORS,
390f76ee892STomi Valkeinen &errors);
391f76ee892STomi Valkeinen
392f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
393f76ee892STomi Valkeinen } else {
394f76ee892STomi Valkeinen r = -ENODEV;
395f76ee892STomi Valkeinen }
396f76ee892STomi Valkeinen
397f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
398f76ee892STomi Valkeinen
399f76ee892STomi Valkeinen if (r)
400f76ee892STomi Valkeinen return r;
401f76ee892STomi Valkeinen
402f63658a5SJing Yao return sysfs_emit(buf, "%d\n", errors);
403f76ee892STomi Valkeinen }
404f76ee892STomi Valkeinen
dsicm_hw_revision_show(struct device * dev,struct device_attribute * attr,char * buf)405f76ee892STomi Valkeinen static ssize_t dsicm_hw_revision_show(struct device *dev,
406f76ee892STomi Valkeinen struct device_attribute *attr, char *buf)
407f76ee892STomi Valkeinen {
4086677b275SWolfram Sang struct panel_drv_data *ddata = dev_get_drvdata(dev);
409f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
410f76ee892STomi Valkeinen u8 id1, id2, id3;
411f76ee892STomi Valkeinen int r;
412f76ee892STomi Valkeinen
413f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
414f76ee892STomi Valkeinen
415f76ee892STomi Valkeinen if (ddata->enabled) {
416f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
417f76ee892STomi Valkeinen
418f76ee892STomi Valkeinen r = dsicm_wake_up(ddata);
419f76ee892STomi Valkeinen if (!r)
420f76ee892STomi Valkeinen r = dsicm_get_id(ddata, &id1, &id2, &id3);
421f76ee892STomi Valkeinen
422f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
423f76ee892STomi Valkeinen } else {
424f76ee892STomi Valkeinen r = -ENODEV;
425f76ee892STomi Valkeinen }
426f76ee892STomi Valkeinen
427f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
428f76ee892STomi Valkeinen
429f76ee892STomi Valkeinen if (r)
430f76ee892STomi Valkeinen return r;
431f76ee892STomi Valkeinen
432f63658a5SJing Yao return sysfs_emit(buf, "%02x.%02x.%02x\n", id1, id2, id3);
433f76ee892STomi Valkeinen }
434f76ee892STomi Valkeinen
dsicm_store_ulps(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)435f76ee892STomi Valkeinen static ssize_t dsicm_store_ulps(struct device *dev,
436f76ee892STomi Valkeinen struct device_attribute *attr,
437f76ee892STomi Valkeinen const char *buf, size_t count)
438f76ee892STomi Valkeinen {
4396677b275SWolfram Sang struct panel_drv_data *ddata = dev_get_drvdata(dev);
440f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
441f76ee892STomi Valkeinen unsigned long t;
442f76ee892STomi Valkeinen int r;
443f76ee892STomi Valkeinen
444f76ee892STomi Valkeinen r = kstrtoul(buf, 0, &t);
445f76ee892STomi Valkeinen if (r)
446f76ee892STomi Valkeinen return r;
447f76ee892STomi Valkeinen
448f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
449f76ee892STomi Valkeinen
450f76ee892STomi Valkeinen if (ddata->enabled) {
451f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
452f76ee892STomi Valkeinen
453f76ee892STomi Valkeinen if (t)
454f76ee892STomi Valkeinen r = dsicm_enter_ulps(ddata);
455f76ee892STomi Valkeinen else
456f76ee892STomi Valkeinen r = dsicm_wake_up(ddata);
457f76ee892STomi Valkeinen
458f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
459f76ee892STomi Valkeinen }
460f76ee892STomi Valkeinen
461f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
462f76ee892STomi Valkeinen
463f76ee892STomi Valkeinen if (r)
464f76ee892STomi Valkeinen return r;
465f76ee892STomi Valkeinen
466f76ee892STomi Valkeinen return count;
467f76ee892STomi Valkeinen }
468f76ee892STomi Valkeinen
dsicm_show_ulps(struct device * dev,struct device_attribute * attr,char * buf)469f76ee892STomi Valkeinen static ssize_t dsicm_show_ulps(struct device *dev,
470f76ee892STomi Valkeinen struct device_attribute *attr,
471f76ee892STomi Valkeinen char *buf)
472f76ee892STomi Valkeinen {
4736677b275SWolfram Sang struct panel_drv_data *ddata = dev_get_drvdata(dev);
474f76ee892STomi Valkeinen unsigned t;
475f76ee892STomi Valkeinen
476f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
477f76ee892STomi Valkeinen t = ddata->ulps_enabled;
478f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
479f76ee892STomi Valkeinen
480f63658a5SJing Yao return sysfs_emit(buf, "%u\n", t);
481f76ee892STomi Valkeinen }
482f76ee892STomi Valkeinen
dsicm_store_ulps_timeout(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)483f76ee892STomi Valkeinen static ssize_t dsicm_store_ulps_timeout(struct device *dev,
484f76ee892STomi Valkeinen struct device_attribute *attr,
485f76ee892STomi Valkeinen const char *buf, size_t count)
486f76ee892STomi Valkeinen {
4876677b275SWolfram Sang struct panel_drv_data *ddata = dev_get_drvdata(dev);
488f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
489f76ee892STomi Valkeinen unsigned long t;
490f76ee892STomi Valkeinen int r;
491f76ee892STomi Valkeinen
492f76ee892STomi Valkeinen r = kstrtoul(buf, 0, &t);
493f76ee892STomi Valkeinen if (r)
494f76ee892STomi Valkeinen return r;
495f76ee892STomi Valkeinen
496f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
497f76ee892STomi Valkeinen ddata->ulps_timeout = t;
498f76ee892STomi Valkeinen
499f76ee892STomi Valkeinen if (ddata->enabled) {
500f76ee892STomi Valkeinen /* dsicm_wake_up will restart the timer */
501f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
502f76ee892STomi Valkeinen r = dsicm_wake_up(ddata);
503f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
504f76ee892STomi Valkeinen }
505f76ee892STomi Valkeinen
506f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
507f76ee892STomi Valkeinen
508f76ee892STomi Valkeinen if (r)
509f76ee892STomi Valkeinen return r;
510f76ee892STomi Valkeinen
511f76ee892STomi Valkeinen return count;
512f76ee892STomi Valkeinen }
513f76ee892STomi Valkeinen
dsicm_show_ulps_timeout(struct device * dev,struct device_attribute * attr,char * buf)514f76ee892STomi Valkeinen static ssize_t dsicm_show_ulps_timeout(struct device *dev,
515f76ee892STomi Valkeinen struct device_attribute *attr,
516f76ee892STomi Valkeinen char *buf)
517f76ee892STomi Valkeinen {
5186677b275SWolfram Sang struct panel_drv_data *ddata = dev_get_drvdata(dev);
519f76ee892STomi Valkeinen unsigned t;
520f76ee892STomi Valkeinen
521f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
522f76ee892STomi Valkeinen t = ddata->ulps_timeout;
523f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
524f76ee892STomi Valkeinen
525f63658a5SJing Yao return sysfs_emit(buf, "%u\n", t);
526f76ee892STomi Valkeinen }
527f76ee892STomi Valkeinen
528f76ee892STomi Valkeinen static DEVICE_ATTR(num_dsi_errors, S_IRUGO, dsicm_num_errors_show, NULL);
529f76ee892STomi Valkeinen static DEVICE_ATTR(hw_revision, S_IRUGO, dsicm_hw_revision_show, NULL);
530f76ee892STomi Valkeinen static DEVICE_ATTR(ulps, S_IRUGO | S_IWUSR,
531f76ee892STomi Valkeinen dsicm_show_ulps, dsicm_store_ulps);
532f76ee892STomi Valkeinen static DEVICE_ATTR(ulps_timeout, S_IRUGO | S_IWUSR,
533f76ee892STomi Valkeinen dsicm_show_ulps_timeout, dsicm_store_ulps_timeout);
534f76ee892STomi Valkeinen
535f76ee892STomi Valkeinen static struct attribute *dsicm_attrs[] = {
536f76ee892STomi Valkeinen &dev_attr_num_dsi_errors.attr,
537f76ee892STomi Valkeinen &dev_attr_hw_revision.attr,
538f76ee892STomi Valkeinen &dev_attr_ulps.attr,
539f76ee892STomi Valkeinen &dev_attr_ulps_timeout.attr,
540f76ee892STomi Valkeinen NULL,
541f76ee892STomi Valkeinen };
542f76ee892STomi Valkeinen
5435ffe80c1SArvind Yadav static const struct attribute_group dsicm_attr_group = {
544f76ee892STomi Valkeinen .attrs = dsicm_attrs,
545f76ee892STomi Valkeinen };
546f76ee892STomi Valkeinen
dsicm_hw_reset(struct panel_drv_data * ddata)547f76ee892STomi Valkeinen static void dsicm_hw_reset(struct panel_drv_data *ddata)
548f76ee892STomi Valkeinen {
54967c366deSDmitry Torokhov /*
55067c366deSDmitry Torokhov * Note that we appear to activate the reset line here. However
55167c366deSDmitry Torokhov * existing DTSes specified incorrect polarity for it (active high),
55267c366deSDmitry Torokhov * so in fact this deasserts the reset line.
55367c366deSDmitry Torokhov */
55467c366deSDmitry Torokhov gpiod_set_value_cansleep(ddata->reset_gpio, 1);
555f76ee892STomi Valkeinen udelay(10);
556f76ee892STomi Valkeinen /* reset the panel */
55767c366deSDmitry Torokhov gpiod_set_value_cansleep(ddata->reset_gpio, 0);
55867c366deSDmitry Torokhov /* keep reset asserted */
559f76ee892STomi Valkeinen udelay(10);
56067c366deSDmitry Torokhov /* release reset line */
56167c366deSDmitry Torokhov gpiod_set_value_cansleep(ddata->reset_gpio, 1);
562f76ee892STomi Valkeinen /* wait after releasing reset */
563f76ee892STomi Valkeinen usleep_range(5000, 10000);
564f76ee892STomi Valkeinen }
565f76ee892STomi Valkeinen
dsicm_power_on(struct panel_drv_data * ddata)566f76ee892STomi Valkeinen static int dsicm_power_on(struct panel_drv_data *ddata)
567f76ee892STomi Valkeinen {
568f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
569f76ee892STomi Valkeinen u8 id1, id2, id3;
570f76ee892STomi Valkeinen int r;
571f76ee892STomi Valkeinen struct omap_dss_dsi_config dsi_config = {
572f76ee892STomi Valkeinen .mode = OMAP_DSS_DSI_CMD_MODE,
573f76ee892STomi Valkeinen .pixel_format = OMAP_DSS_DSI_FMT_RGB888,
574f76ee892STomi Valkeinen .timings = &ddata->timings,
575f76ee892STomi Valkeinen .hs_clk_min = 150000000,
576f76ee892STomi Valkeinen .hs_clk_max = 300000000,
577f76ee892STomi Valkeinen .lp_clk_min = 7000000,
578f76ee892STomi Valkeinen .lp_clk_max = 10000000,
579f76ee892STomi Valkeinen };
580f76ee892STomi Valkeinen
581f76ee892STomi Valkeinen if (ddata->pin_config.num_pins > 0) {
582f76ee892STomi Valkeinen r = in->ops.dsi->configure_pins(in, &ddata->pin_config);
583f76ee892STomi Valkeinen if (r) {
584f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev,
585f76ee892STomi Valkeinen "failed to configure DSI pins\n");
586f76ee892STomi Valkeinen goto err0;
587f76ee892STomi Valkeinen }
588f76ee892STomi Valkeinen }
589f76ee892STomi Valkeinen
590f76ee892STomi Valkeinen r = in->ops.dsi->set_config(in, &dsi_config);
591f76ee892STomi Valkeinen if (r) {
592f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "failed to configure DSI\n");
593f76ee892STomi Valkeinen goto err0;
594f76ee892STomi Valkeinen }
595f76ee892STomi Valkeinen
596f76ee892STomi Valkeinen r = in->ops.dsi->enable(in);
597f76ee892STomi Valkeinen if (r) {
598f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "failed to enable DSI\n");
599f76ee892STomi Valkeinen goto err0;
600f76ee892STomi Valkeinen }
601f76ee892STomi Valkeinen
602f76ee892STomi Valkeinen dsicm_hw_reset(ddata);
603f76ee892STomi Valkeinen
604f76ee892STomi Valkeinen in->ops.dsi->enable_hs(in, ddata->channel, false);
605f76ee892STomi Valkeinen
606f76ee892STomi Valkeinen r = dsicm_sleep_out(ddata);
607f76ee892STomi Valkeinen if (r)
608f76ee892STomi Valkeinen goto err;
609f76ee892STomi Valkeinen
610f76ee892STomi Valkeinen r = dsicm_get_id(ddata, &id1, &id2, &id3);
611f76ee892STomi Valkeinen if (r)
612f76ee892STomi Valkeinen goto err;
613f76ee892STomi Valkeinen
614f76ee892STomi Valkeinen r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, 0xff);
615f76ee892STomi Valkeinen if (r)
616f76ee892STomi Valkeinen goto err;
617f76ee892STomi Valkeinen
618f76ee892STomi Valkeinen r = dsicm_dcs_write_1(ddata, DCS_CTRL_DISPLAY,
619f76ee892STomi Valkeinen (1<<2) | (1<<5)); /* BL | BCTRL */
620f76ee892STomi Valkeinen if (r)
621f76ee892STomi Valkeinen goto err;
622f76ee892STomi Valkeinen
623f76ee892STomi Valkeinen r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_PIXEL_FORMAT,
624f76ee892STomi Valkeinen MIPI_DCS_PIXEL_FMT_24BIT);
625f76ee892STomi Valkeinen if (r)
626f76ee892STomi Valkeinen goto err;
627f76ee892STomi Valkeinen
628f76ee892STomi Valkeinen r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_DISPLAY_ON);
629f76ee892STomi Valkeinen if (r)
630f76ee892STomi Valkeinen goto err;
631f76ee892STomi Valkeinen
632f76ee892STomi Valkeinen r = _dsicm_enable_te(ddata, ddata->te_enabled);
633f76ee892STomi Valkeinen if (r)
634f76ee892STomi Valkeinen goto err;
635f76ee892STomi Valkeinen
636f76ee892STomi Valkeinen r = in->ops.dsi->enable_video_output(in, ddata->channel);
637f76ee892STomi Valkeinen if (r)
638f76ee892STomi Valkeinen goto err;
639f76ee892STomi Valkeinen
640f76ee892STomi Valkeinen ddata->enabled = 1;
641f76ee892STomi Valkeinen
642f76ee892STomi Valkeinen if (!ddata->intro_printed) {
643f76ee892STomi Valkeinen dev_info(&ddata->pdev->dev, "panel revision %02x.%02x.%02x\n",
644f76ee892STomi Valkeinen id1, id2, id3);
645f76ee892STomi Valkeinen ddata->intro_printed = true;
646f76ee892STomi Valkeinen }
647f76ee892STomi Valkeinen
648f76ee892STomi Valkeinen in->ops.dsi->enable_hs(in, ddata->channel, true);
649f76ee892STomi Valkeinen
650f76ee892STomi Valkeinen return 0;
651f76ee892STomi Valkeinen err:
652f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "error while enabling panel, issuing HW reset\n");
653f76ee892STomi Valkeinen
654f76ee892STomi Valkeinen dsicm_hw_reset(ddata);
655f76ee892STomi Valkeinen
656f76ee892STomi Valkeinen in->ops.dsi->disable(in, true, false);
657f76ee892STomi Valkeinen err0:
658f76ee892STomi Valkeinen return r;
659f76ee892STomi Valkeinen }
660f76ee892STomi Valkeinen
dsicm_power_off(struct panel_drv_data * ddata)661f76ee892STomi Valkeinen static void dsicm_power_off(struct panel_drv_data *ddata)
662f76ee892STomi Valkeinen {
663f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
664f76ee892STomi Valkeinen int r;
665f76ee892STomi Valkeinen
666f76ee892STomi Valkeinen in->ops.dsi->disable_video_output(in, ddata->channel);
667f76ee892STomi Valkeinen
668f76ee892STomi Valkeinen r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_DISPLAY_OFF);
669f76ee892STomi Valkeinen if (!r)
670f76ee892STomi Valkeinen r = dsicm_sleep_in(ddata);
671f76ee892STomi Valkeinen
672f76ee892STomi Valkeinen if (r) {
673f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev,
674f76ee892STomi Valkeinen "error disabling panel, issuing HW reset\n");
675f76ee892STomi Valkeinen dsicm_hw_reset(ddata);
676f76ee892STomi Valkeinen }
677f76ee892STomi Valkeinen
678f76ee892STomi Valkeinen in->ops.dsi->disable(in, true, false);
679f76ee892STomi Valkeinen
680f76ee892STomi Valkeinen ddata->enabled = 0;
681f76ee892STomi Valkeinen }
682f76ee892STomi Valkeinen
dsicm_panel_reset(struct panel_drv_data * ddata)683f76ee892STomi Valkeinen static int dsicm_panel_reset(struct panel_drv_data *ddata)
684f76ee892STomi Valkeinen {
685f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "performing LCD reset\n");
686f76ee892STomi Valkeinen
687f76ee892STomi Valkeinen dsicm_power_off(ddata);
688f76ee892STomi Valkeinen dsicm_hw_reset(ddata);
689f76ee892STomi Valkeinen return dsicm_power_on(ddata);
690f76ee892STomi Valkeinen }
691f76ee892STomi Valkeinen
dsicm_connect(struct omap_dss_device * dssdev)692f76ee892STomi Valkeinen static int dsicm_connect(struct omap_dss_device *dssdev)
693f76ee892STomi Valkeinen {
694f76ee892STomi Valkeinen struct panel_drv_data *ddata = to_panel_data(dssdev);
695f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
696f76ee892STomi Valkeinen struct device *dev = &ddata->pdev->dev;
697f76ee892STomi Valkeinen int r;
698f76ee892STomi Valkeinen
699f76ee892STomi Valkeinen if (omapdss_device_is_connected(dssdev))
700f76ee892STomi Valkeinen return 0;
701f76ee892STomi Valkeinen
702f76ee892STomi Valkeinen r = in->ops.dsi->connect(in, dssdev);
703f76ee892STomi Valkeinen if (r) {
704f76ee892STomi Valkeinen dev_err(dev, "Failed to connect to video source\n");
705f76ee892STomi Valkeinen return r;
706f76ee892STomi Valkeinen }
707f76ee892STomi Valkeinen
708f76ee892STomi Valkeinen r = in->ops.dsi->request_vc(ddata->in, &ddata->channel);
709f76ee892STomi Valkeinen if (r) {
710f76ee892STomi Valkeinen dev_err(dev, "failed to get virtual channel\n");
711f76ee892STomi Valkeinen goto err_req_vc;
712f76ee892STomi Valkeinen }
713f76ee892STomi Valkeinen
714f76ee892STomi Valkeinen r = in->ops.dsi->set_vc_id(ddata->in, ddata->channel, TCH);
715f76ee892STomi Valkeinen if (r) {
716f76ee892STomi Valkeinen dev_err(dev, "failed to set VC_ID\n");
717f76ee892STomi Valkeinen goto err_vc_id;
718f76ee892STomi Valkeinen }
719f76ee892STomi Valkeinen
720f76ee892STomi Valkeinen return 0;
721f76ee892STomi Valkeinen
722f76ee892STomi Valkeinen err_vc_id:
723f76ee892STomi Valkeinen in->ops.dsi->release_vc(ddata->in, ddata->channel);
724f76ee892STomi Valkeinen err_req_vc:
725f76ee892STomi Valkeinen in->ops.dsi->disconnect(in, dssdev);
726f76ee892STomi Valkeinen return r;
727f76ee892STomi Valkeinen }
728f76ee892STomi Valkeinen
dsicm_disconnect(struct omap_dss_device * dssdev)729f76ee892STomi Valkeinen static void dsicm_disconnect(struct omap_dss_device *dssdev)
730f76ee892STomi Valkeinen {
731f76ee892STomi Valkeinen struct panel_drv_data *ddata = to_panel_data(dssdev);
732f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
733f76ee892STomi Valkeinen
734f76ee892STomi Valkeinen if (!omapdss_device_is_connected(dssdev))
735f76ee892STomi Valkeinen return;
736f76ee892STomi Valkeinen
737f76ee892STomi Valkeinen in->ops.dsi->release_vc(in, ddata->channel);
738f76ee892STomi Valkeinen in->ops.dsi->disconnect(in, dssdev);
739f76ee892STomi Valkeinen }
740f76ee892STomi Valkeinen
dsicm_enable(struct omap_dss_device * dssdev)741f76ee892STomi Valkeinen static int dsicm_enable(struct omap_dss_device *dssdev)
742f76ee892STomi Valkeinen {
743f76ee892STomi Valkeinen struct panel_drv_data *ddata = to_panel_data(dssdev);
744f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
745f76ee892STomi Valkeinen int r;
746f76ee892STomi Valkeinen
747f76ee892STomi Valkeinen dev_dbg(&ddata->pdev->dev, "enable\n");
748f76ee892STomi Valkeinen
749f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
750f76ee892STomi Valkeinen
751f76ee892STomi Valkeinen if (!omapdss_device_is_connected(dssdev)) {
752f76ee892STomi Valkeinen r = -ENODEV;
753f76ee892STomi Valkeinen goto err;
754f76ee892STomi Valkeinen }
755f76ee892STomi Valkeinen
756f76ee892STomi Valkeinen if (omapdss_device_is_enabled(dssdev)) {
757f76ee892STomi Valkeinen r = 0;
758f76ee892STomi Valkeinen goto err;
759f76ee892STomi Valkeinen }
760f76ee892STomi Valkeinen
761f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
762f76ee892STomi Valkeinen
763f76ee892STomi Valkeinen r = dsicm_power_on(ddata);
764f76ee892STomi Valkeinen
765f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
766f76ee892STomi Valkeinen
767f76ee892STomi Valkeinen if (r)
768f76ee892STomi Valkeinen goto err;
769f76ee892STomi Valkeinen
770f76ee892STomi Valkeinen dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
771f76ee892STomi Valkeinen
772f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
773f76ee892STomi Valkeinen
774f76ee892STomi Valkeinen return 0;
775f76ee892STomi Valkeinen err:
776f76ee892STomi Valkeinen dev_dbg(&ddata->pdev->dev, "enable failed\n");
777f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
778f76ee892STomi Valkeinen return r;
779f76ee892STomi Valkeinen }
780f76ee892STomi Valkeinen
dsicm_disable(struct omap_dss_device * dssdev)781f76ee892STomi Valkeinen static void dsicm_disable(struct omap_dss_device *dssdev)
782f76ee892STomi Valkeinen {
783f76ee892STomi Valkeinen struct panel_drv_data *ddata = to_panel_data(dssdev);
784f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
785f76ee892STomi Valkeinen int r;
786f76ee892STomi Valkeinen
787f76ee892STomi Valkeinen dev_dbg(&ddata->pdev->dev, "disable\n");
788f76ee892STomi Valkeinen
789f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
790f76ee892STomi Valkeinen
791f76ee892STomi Valkeinen dsicm_cancel_ulps_work(ddata);
792f76ee892STomi Valkeinen
793f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
794f76ee892STomi Valkeinen
795f76ee892STomi Valkeinen if (omapdss_device_is_enabled(dssdev)) {
796f76ee892STomi Valkeinen r = dsicm_wake_up(ddata);
797f76ee892STomi Valkeinen if (!r)
798f76ee892STomi Valkeinen dsicm_power_off(ddata);
799f76ee892STomi Valkeinen }
800f76ee892STomi Valkeinen
801f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
802f76ee892STomi Valkeinen
803f76ee892STomi Valkeinen dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
804f76ee892STomi Valkeinen
805f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
806f76ee892STomi Valkeinen }
807f76ee892STomi Valkeinen
dsicm_framedone_cb(int err,void * data)808f76ee892STomi Valkeinen static void dsicm_framedone_cb(int err, void *data)
809f76ee892STomi Valkeinen {
810f76ee892STomi Valkeinen struct panel_drv_data *ddata = data;
811f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
812f76ee892STomi Valkeinen
813f76ee892STomi Valkeinen dev_dbg(&ddata->pdev->dev, "framedone, err %d\n", err);
814f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(ddata->in);
815f76ee892STomi Valkeinen }
816f76ee892STomi Valkeinen
dsicm_te_isr(int irq,void * data)817f76ee892STomi Valkeinen static irqreturn_t dsicm_te_isr(int irq, void *data)
818f76ee892STomi Valkeinen {
819f76ee892STomi Valkeinen struct panel_drv_data *ddata = data;
820f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
821f76ee892STomi Valkeinen int old;
822f76ee892STomi Valkeinen int r;
823f76ee892STomi Valkeinen
824f76ee892STomi Valkeinen old = atomic_cmpxchg(&ddata->do_update, 1, 0);
825f76ee892STomi Valkeinen
826f76ee892STomi Valkeinen if (old) {
827f76ee892STomi Valkeinen cancel_delayed_work(&ddata->te_timeout_work);
828f76ee892STomi Valkeinen
829f76ee892STomi Valkeinen r = in->ops.dsi->update(in, ddata->channel, dsicm_framedone_cb,
830f76ee892STomi Valkeinen ddata);
831f76ee892STomi Valkeinen if (r)
832f76ee892STomi Valkeinen goto err;
833f76ee892STomi Valkeinen }
834f76ee892STomi Valkeinen
835f76ee892STomi Valkeinen return IRQ_HANDLED;
836f76ee892STomi Valkeinen err:
837f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "start update failed\n");
838f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
839f76ee892STomi Valkeinen return IRQ_HANDLED;
840f76ee892STomi Valkeinen }
841f76ee892STomi Valkeinen
dsicm_te_timeout_work_callback(struct work_struct * work)842f76ee892STomi Valkeinen static void dsicm_te_timeout_work_callback(struct work_struct *work)
843f76ee892STomi Valkeinen {
844f76ee892STomi Valkeinen struct panel_drv_data *ddata = container_of(work, struct panel_drv_data,
845f76ee892STomi Valkeinen te_timeout_work.work);
846f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
847f76ee892STomi Valkeinen
848f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "TE not received for 250ms!\n");
849f76ee892STomi Valkeinen
850f76ee892STomi Valkeinen atomic_set(&ddata->do_update, 0);
851f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
852f76ee892STomi Valkeinen }
853f76ee892STomi Valkeinen
dsicm_update(struct omap_dss_device * dssdev,u16 x,u16 y,u16 w,u16 h)854f76ee892STomi Valkeinen static int dsicm_update(struct omap_dss_device *dssdev,
855f76ee892STomi Valkeinen u16 x, u16 y, u16 w, u16 h)
856f76ee892STomi Valkeinen {
857f76ee892STomi Valkeinen struct panel_drv_data *ddata = to_panel_data(dssdev);
858f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
859f76ee892STomi Valkeinen int r;
860f76ee892STomi Valkeinen
861f76ee892STomi Valkeinen dev_dbg(&ddata->pdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
862f76ee892STomi Valkeinen
863f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
864f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
865f76ee892STomi Valkeinen
866f76ee892STomi Valkeinen r = dsicm_wake_up(ddata);
867f76ee892STomi Valkeinen if (r)
868f76ee892STomi Valkeinen goto err;
869f76ee892STomi Valkeinen
870f76ee892STomi Valkeinen if (!ddata->enabled) {
871f76ee892STomi Valkeinen r = 0;
872f76ee892STomi Valkeinen goto err;
873f76ee892STomi Valkeinen }
874f76ee892STomi Valkeinen
875f76ee892STomi Valkeinen /* XXX no need to send this every frame, but dsi break if not done */
876f76ee892STomi Valkeinen r = dsicm_set_update_window(ddata, 0, 0,
877f76ee892STomi Valkeinen dssdev->panel.timings.x_res,
878f76ee892STomi Valkeinen dssdev->panel.timings.y_res);
879f76ee892STomi Valkeinen if (r)
880f76ee892STomi Valkeinen goto err;
881f76ee892STomi Valkeinen
88267c366deSDmitry Torokhov if (ddata->te_enabled && ddata->ext_te_gpio) {
883f76ee892STomi Valkeinen schedule_delayed_work(&ddata->te_timeout_work,
884f76ee892STomi Valkeinen msecs_to_jiffies(250));
885f76ee892STomi Valkeinen atomic_set(&ddata->do_update, 1);
886f76ee892STomi Valkeinen } else {
887f76ee892STomi Valkeinen r = in->ops.dsi->update(in, ddata->channel, dsicm_framedone_cb,
888f76ee892STomi Valkeinen ddata);
889f76ee892STomi Valkeinen if (r)
890f76ee892STomi Valkeinen goto err;
891f76ee892STomi Valkeinen }
892f76ee892STomi Valkeinen
893f76ee892STomi Valkeinen /* note: no bus_unlock here. unlock is in framedone_cb */
894f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
895f76ee892STomi Valkeinen return 0;
896f76ee892STomi Valkeinen err:
897f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
898f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
899f76ee892STomi Valkeinen return r;
900f76ee892STomi Valkeinen }
901f76ee892STomi Valkeinen
dsicm_sync(struct omap_dss_device * dssdev)902f76ee892STomi Valkeinen static int dsicm_sync(struct omap_dss_device *dssdev)
903f76ee892STomi Valkeinen {
904f76ee892STomi Valkeinen struct panel_drv_data *ddata = to_panel_data(dssdev);
905f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
906f76ee892STomi Valkeinen
907f76ee892STomi Valkeinen dev_dbg(&ddata->pdev->dev, "sync\n");
908f76ee892STomi Valkeinen
909f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
910f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
911f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
912f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
913f76ee892STomi Valkeinen
914f76ee892STomi Valkeinen dev_dbg(&ddata->pdev->dev, "sync done\n");
915f76ee892STomi Valkeinen
916f76ee892STomi Valkeinen return 0;
917f76ee892STomi Valkeinen }
918f76ee892STomi Valkeinen
_dsicm_enable_te(struct panel_drv_data * ddata,bool enable)919f76ee892STomi Valkeinen static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable)
920f76ee892STomi Valkeinen {
921f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
922f76ee892STomi Valkeinen int r;
923f76ee892STomi Valkeinen
924f76ee892STomi Valkeinen if (enable)
925f76ee892STomi Valkeinen r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_TEAR_ON, 0);
926f76ee892STomi Valkeinen else
927f76ee892STomi Valkeinen r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_TEAR_OFF);
928f76ee892STomi Valkeinen
92967c366deSDmitry Torokhov if (!ddata->ext_te_gpio)
930f76ee892STomi Valkeinen in->ops.dsi->enable_te(in, enable);
931f76ee892STomi Valkeinen
932f76ee892STomi Valkeinen /* possible panel bug */
933f76ee892STomi Valkeinen msleep(100);
934f76ee892STomi Valkeinen
935f76ee892STomi Valkeinen return r;
936f76ee892STomi Valkeinen }
937f76ee892STomi Valkeinen
dsicm_enable_te(struct omap_dss_device * dssdev,bool enable)938f76ee892STomi Valkeinen static int dsicm_enable_te(struct omap_dss_device *dssdev, bool enable)
939f76ee892STomi Valkeinen {
940f76ee892STomi Valkeinen struct panel_drv_data *ddata = to_panel_data(dssdev);
941f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
942f76ee892STomi Valkeinen int r;
943f76ee892STomi Valkeinen
944f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
945f76ee892STomi Valkeinen
946f76ee892STomi Valkeinen if (ddata->te_enabled == enable)
947f76ee892STomi Valkeinen goto end;
948f76ee892STomi Valkeinen
949f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
950f76ee892STomi Valkeinen
951f76ee892STomi Valkeinen if (ddata->enabled) {
952f76ee892STomi Valkeinen r = dsicm_wake_up(ddata);
953f76ee892STomi Valkeinen if (r)
954f76ee892STomi Valkeinen goto err;
955f76ee892STomi Valkeinen
956f76ee892STomi Valkeinen r = _dsicm_enable_te(ddata, enable);
957f76ee892STomi Valkeinen if (r)
958f76ee892STomi Valkeinen goto err;
959f76ee892STomi Valkeinen }
960f76ee892STomi Valkeinen
961f76ee892STomi Valkeinen ddata->te_enabled = enable;
962f76ee892STomi Valkeinen
963f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
964f76ee892STomi Valkeinen end:
965f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
966f76ee892STomi Valkeinen
967f76ee892STomi Valkeinen return 0;
968f76ee892STomi Valkeinen err:
969f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
970f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
971f76ee892STomi Valkeinen
972f76ee892STomi Valkeinen return r;
973f76ee892STomi Valkeinen }
974f76ee892STomi Valkeinen
dsicm_get_te(struct omap_dss_device * dssdev)975f76ee892STomi Valkeinen static int dsicm_get_te(struct omap_dss_device *dssdev)
976f76ee892STomi Valkeinen {
977f76ee892STomi Valkeinen struct panel_drv_data *ddata = to_panel_data(dssdev);
978f76ee892STomi Valkeinen int r;
979f76ee892STomi Valkeinen
980f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
981f76ee892STomi Valkeinen r = ddata->te_enabled;
982f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
983f76ee892STomi Valkeinen
984f76ee892STomi Valkeinen return r;
985f76ee892STomi Valkeinen }
986f76ee892STomi Valkeinen
dsicm_memory_read(struct omap_dss_device * dssdev,void * buf,size_t size,u16 x,u16 y,u16 w,u16 h)987f76ee892STomi Valkeinen static int dsicm_memory_read(struct omap_dss_device *dssdev,
988f76ee892STomi Valkeinen void *buf, size_t size,
989f76ee892STomi Valkeinen u16 x, u16 y, u16 w, u16 h)
990f76ee892STomi Valkeinen {
991f76ee892STomi Valkeinen struct panel_drv_data *ddata = to_panel_data(dssdev);
992f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
993f76ee892STomi Valkeinen int r;
994f76ee892STomi Valkeinen int first = 1;
995f76ee892STomi Valkeinen int plen;
996f76ee892STomi Valkeinen unsigned buf_used = 0;
997f76ee892STomi Valkeinen
998f76ee892STomi Valkeinen if (size < w * h * 3)
999f76ee892STomi Valkeinen return -ENOMEM;
1000f76ee892STomi Valkeinen
1001f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
1002f76ee892STomi Valkeinen
1003f76ee892STomi Valkeinen if (!ddata->enabled) {
1004f76ee892STomi Valkeinen r = -ENODEV;
1005f76ee892STomi Valkeinen goto err1;
1006f76ee892STomi Valkeinen }
1007f76ee892STomi Valkeinen
1008f76ee892STomi Valkeinen size = min(w * h * 3,
1009f76ee892STomi Valkeinen dssdev->panel.timings.x_res *
1010f76ee892STomi Valkeinen dssdev->panel.timings.y_res * 3);
1011f76ee892STomi Valkeinen
1012f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
1013f76ee892STomi Valkeinen
1014f76ee892STomi Valkeinen r = dsicm_wake_up(ddata);
1015f76ee892STomi Valkeinen if (r)
1016f76ee892STomi Valkeinen goto err2;
1017f76ee892STomi Valkeinen
1018f76ee892STomi Valkeinen /* plen 1 or 2 goes into short packet. until checksum error is fixed,
1019f76ee892STomi Valkeinen * use short packets. plen 32 works, but bigger packets seem to cause
1020f76ee892STomi Valkeinen * an error. */
1021f76ee892STomi Valkeinen if (size % 2)
1022f76ee892STomi Valkeinen plen = 1;
1023f76ee892STomi Valkeinen else
1024f76ee892STomi Valkeinen plen = 2;
1025f76ee892STomi Valkeinen
1026f76ee892STomi Valkeinen dsicm_set_update_window(ddata, x, y, w, h);
1027f76ee892STomi Valkeinen
1028f76ee892STomi Valkeinen r = in->ops.dsi->set_max_rx_packet_size(in, ddata->channel, plen);
1029f76ee892STomi Valkeinen if (r)
1030f76ee892STomi Valkeinen goto err2;
1031f76ee892STomi Valkeinen
1032f76ee892STomi Valkeinen while (buf_used < size) {
1033f76ee892STomi Valkeinen u8 dcs_cmd = first ? 0x2e : 0x3e;
1034f76ee892STomi Valkeinen first = 0;
1035f76ee892STomi Valkeinen
1036f76ee892STomi Valkeinen r = in->ops.dsi->dcs_read(in, ddata->channel, dcs_cmd,
1037f76ee892STomi Valkeinen buf + buf_used, size - buf_used);
1038f76ee892STomi Valkeinen
1039f76ee892STomi Valkeinen if (r < 0) {
1040f76ee892STomi Valkeinen dev_err(dssdev->dev, "read error\n");
1041f76ee892STomi Valkeinen goto err3;
1042f76ee892STomi Valkeinen }
1043f76ee892STomi Valkeinen
1044f76ee892STomi Valkeinen buf_used += r;
1045f76ee892STomi Valkeinen
1046f76ee892STomi Valkeinen if (r < plen) {
1047f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "short read\n");
1048f76ee892STomi Valkeinen break;
1049f76ee892STomi Valkeinen }
1050f76ee892STomi Valkeinen
1051f76ee892STomi Valkeinen if (signal_pending(current)) {
1052f76ee892STomi Valkeinen dev_err(&ddata->pdev->dev, "signal pending, "
1053f76ee892STomi Valkeinen "aborting memory read\n");
1054f76ee892STomi Valkeinen r = -ERESTARTSYS;
1055f76ee892STomi Valkeinen goto err3;
1056f76ee892STomi Valkeinen }
1057f76ee892STomi Valkeinen }
1058f76ee892STomi Valkeinen
1059f76ee892STomi Valkeinen r = buf_used;
1060f76ee892STomi Valkeinen
1061f76ee892STomi Valkeinen err3:
1062f76ee892STomi Valkeinen in->ops.dsi->set_max_rx_packet_size(in, ddata->channel, 1);
1063f76ee892STomi Valkeinen err2:
1064f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
1065f76ee892STomi Valkeinen err1:
1066f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
1067f76ee892STomi Valkeinen return r;
1068f76ee892STomi Valkeinen }
1069f76ee892STomi Valkeinen
dsicm_ulps_work(struct work_struct * work)1070f76ee892STomi Valkeinen static void dsicm_ulps_work(struct work_struct *work)
1071f76ee892STomi Valkeinen {
1072f76ee892STomi Valkeinen struct panel_drv_data *ddata = container_of(work, struct panel_drv_data,
1073f76ee892STomi Valkeinen ulps_work.work);
1074f76ee892STomi Valkeinen struct omap_dss_device *dssdev = &ddata->dssdev;
1075f76ee892STomi Valkeinen struct omap_dss_device *in = ddata->in;
1076f76ee892STomi Valkeinen
1077f76ee892STomi Valkeinen mutex_lock(&ddata->lock);
1078f76ee892STomi Valkeinen
1079f76ee892STomi Valkeinen if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || !ddata->enabled) {
1080f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
1081f76ee892STomi Valkeinen return;
1082f76ee892STomi Valkeinen }
1083f76ee892STomi Valkeinen
1084f76ee892STomi Valkeinen in->ops.dsi->bus_lock(in);
1085f76ee892STomi Valkeinen
1086f76ee892STomi Valkeinen dsicm_enter_ulps(ddata);
1087f76ee892STomi Valkeinen
1088f76ee892STomi Valkeinen in->ops.dsi->bus_unlock(in);
1089f76ee892STomi Valkeinen mutex_unlock(&ddata->lock);
1090f76ee892STomi Valkeinen }
1091f76ee892STomi Valkeinen
1092f76ee892STomi Valkeinen static struct omap_dss_driver dsicm_ops = {
1093f76ee892STomi Valkeinen .connect = dsicm_connect,
1094f76ee892STomi Valkeinen .disconnect = dsicm_disconnect,
1095f76ee892STomi Valkeinen
1096f76ee892STomi Valkeinen .enable = dsicm_enable,
1097f76ee892STomi Valkeinen .disable = dsicm_disable,
1098f76ee892STomi Valkeinen
1099f76ee892STomi Valkeinen .update = dsicm_update,
1100f76ee892STomi Valkeinen .sync = dsicm_sync,
1101f76ee892STomi Valkeinen
1102f76ee892STomi Valkeinen .get_resolution = dsicm_get_resolution,
1103f76ee892STomi Valkeinen .get_recommended_bpp = omapdss_default_get_recommended_bpp,
1104f76ee892STomi Valkeinen
1105f76ee892STomi Valkeinen .enable_te = dsicm_enable_te,
1106f76ee892STomi Valkeinen .get_te = dsicm_get_te,
1107f76ee892STomi Valkeinen
1108f76ee892STomi Valkeinen .memory_read = dsicm_memory_read,
1109f76ee892STomi Valkeinen };
1110f76ee892STomi Valkeinen
dsicm_probe(struct platform_device * pdev)1111f76ee892STomi Valkeinen static int dsicm_probe(struct platform_device *pdev)
1112f76ee892STomi Valkeinen {
1113f76ee892STomi Valkeinen struct backlight_properties props;
1114f76ee892STomi Valkeinen struct panel_drv_data *ddata;
1115f76ee892STomi Valkeinen struct backlight_device *bldev = NULL;
1116f76ee892STomi Valkeinen struct device *dev = &pdev->dev;
1117f76ee892STomi Valkeinen struct omap_dss_device *dssdev;
1118f76ee892STomi Valkeinen int r;
1119f76ee892STomi Valkeinen
1120f76ee892STomi Valkeinen dev_dbg(dev, "probe\n");
1121f76ee892STomi Valkeinen
11220984097cSPeter Ujfalusi if (!pdev->dev.of_node)
11230984097cSPeter Ujfalusi return -ENODEV;
11240984097cSPeter Ujfalusi
1125f76ee892STomi Valkeinen ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
1126f76ee892STomi Valkeinen if (!ddata)
1127f76ee892STomi Valkeinen return -ENOMEM;
1128f76ee892STomi Valkeinen
1129f76ee892STomi Valkeinen platform_set_drvdata(pdev, ddata);
1130f76ee892STomi Valkeinen ddata->pdev = pdev;
1131f76ee892STomi Valkeinen
113267c366deSDmitry Torokhov ddata->in = omapdss_of_find_source_for_first_ep(pdev->dev.of_node);
113367c366deSDmitry Torokhov r = PTR_ERR_OR_ZERO(ddata->in);
113467c366deSDmitry Torokhov if (r) {
113567c366deSDmitry Torokhov dev_err(&pdev->dev, "failed to find video source: %d\n", r);
1136f76ee892STomi Valkeinen return r;
113767c366deSDmitry Torokhov }
1138f76ee892STomi Valkeinen
1139f76ee892STomi Valkeinen ddata->timings.x_res = 864;
1140f76ee892STomi Valkeinen ddata->timings.y_res = 480;
1141f76ee892STomi Valkeinen ddata->timings.pixelclock = 864 * 480 * 60;
1142f76ee892STomi Valkeinen
1143f76ee892STomi Valkeinen dssdev = &ddata->dssdev;
1144f76ee892STomi Valkeinen dssdev->dev = dev;
1145f76ee892STomi Valkeinen dssdev->driver = &dsicm_ops;
1146f76ee892STomi Valkeinen dssdev->panel.timings = ddata->timings;
1147f76ee892STomi Valkeinen dssdev->type = OMAP_DISPLAY_TYPE_DSI;
1148f76ee892STomi Valkeinen dssdev->owner = THIS_MODULE;
1149f76ee892STomi Valkeinen
1150f76ee892STomi Valkeinen dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888;
1151f76ee892STomi Valkeinen dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE |
1152f76ee892STomi Valkeinen OMAP_DSS_DISPLAY_CAP_TEAR_ELIM;
1153f76ee892STomi Valkeinen
1154f76ee892STomi Valkeinen r = omapdss_register_display(dssdev);
1155f76ee892STomi Valkeinen if (r) {
1156f76ee892STomi Valkeinen dev_err(dev, "Failed to register panel\n");
1157f76ee892STomi Valkeinen goto err_reg;
1158f76ee892STomi Valkeinen }
1159f76ee892STomi Valkeinen
1160f76ee892STomi Valkeinen mutex_init(&ddata->lock);
1161f76ee892STomi Valkeinen
1162f76ee892STomi Valkeinen atomic_set(&ddata->do_update, 0);
1163f76ee892STomi Valkeinen
116467c366deSDmitry Torokhov ddata->reset_gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW);
116567c366deSDmitry Torokhov r = PTR_ERR_OR_ZERO(ddata->reset_gpio);
1166f76ee892STomi Valkeinen if (r) {
116767c366deSDmitry Torokhov dev_err(&pdev->dev, "Failed to request reset gpio: %d\n", r);
1168f76ee892STomi Valkeinen return r;
1169f76ee892STomi Valkeinen }
1170f76ee892STomi Valkeinen
117167c366deSDmitry Torokhov gpiod_set_consumer_name(ddata->reset_gpio, "taal rst");
117267c366deSDmitry Torokhov
117367c366deSDmitry Torokhov ddata->ext_te_gpio = devm_gpiod_get_optional(&pdev->dev, "te",
117467c366deSDmitry Torokhov GPIOD_IN);
117567c366deSDmitry Torokhov r = PTR_ERR_OR_ZERO(ddata->ext_te_gpio);
117667c366deSDmitry Torokhov if (r) {
117767c366deSDmitry Torokhov dev_err(&pdev->dev, "Failed to request TE gpio: %d\n", r);
117867c366deSDmitry Torokhov return r;
117967c366deSDmitry Torokhov }
118067c366deSDmitry Torokhov
118167c366deSDmitry Torokhov if (ddata->ext_te_gpio) {
118267c366deSDmitry Torokhov gpiod_set_consumer_name(ddata->ext_te_gpio, "taal irq");
118367c366deSDmitry Torokhov
118467c366deSDmitry Torokhov r = devm_request_irq(dev, gpiod_to_irq(ddata->ext_te_gpio),
1185f76ee892STomi Valkeinen dsicm_te_isr,
1186f76ee892STomi Valkeinen IRQF_TRIGGER_RISING,
1187f76ee892STomi Valkeinen "taal vsync", ddata);
1188f76ee892STomi Valkeinen
1189f76ee892STomi Valkeinen if (r) {
1190f76ee892STomi Valkeinen dev_err(dev, "IRQ request failed\n");
1191f76ee892STomi Valkeinen return r;
1192f76ee892STomi Valkeinen }
1193f76ee892STomi Valkeinen
1194f76ee892STomi Valkeinen INIT_DEFERRABLE_WORK(&ddata->te_timeout_work,
1195f76ee892STomi Valkeinen dsicm_te_timeout_work_callback);
1196f76ee892STomi Valkeinen
1197f76ee892STomi Valkeinen dev_dbg(dev, "Using GPIO TE\n");
1198f76ee892STomi Valkeinen }
1199f76ee892STomi Valkeinen
1200f76ee892STomi Valkeinen INIT_DELAYED_WORK(&ddata->ulps_work, dsicm_ulps_work);
1201f76ee892STomi Valkeinen
1202f76ee892STomi Valkeinen dsicm_hw_reset(ddata);
1203f76ee892STomi Valkeinen
1204f76ee892STomi Valkeinen if (ddata->use_dsi_backlight) {
1205f76ee892STomi Valkeinen memset(&props, 0, sizeof(struct backlight_properties));
1206f76ee892STomi Valkeinen props.max_brightness = 255;
1207f76ee892STomi Valkeinen
1208f76ee892STomi Valkeinen props.type = BACKLIGHT_RAW;
1209f76ee892STomi Valkeinen bldev = backlight_device_register(dev_name(dev),
1210f76ee892STomi Valkeinen dev, ddata, &dsicm_bl_ops, &props);
1211f76ee892STomi Valkeinen if (IS_ERR(bldev)) {
1212f76ee892STomi Valkeinen r = PTR_ERR(bldev);
1213b59ac9a3SBhaktipriya Shridhar goto err_reg;
1214f76ee892STomi Valkeinen }
1215f76ee892STomi Valkeinen
1216f76ee892STomi Valkeinen ddata->bldev = bldev;
1217f76ee892STomi Valkeinen
1218f76ee892STomi Valkeinen bldev->props.power = FB_BLANK_UNBLANK;
1219f76ee892STomi Valkeinen bldev->props.brightness = 255;
1220f76ee892STomi Valkeinen
1221f76ee892STomi Valkeinen dsicm_bl_update_status(bldev);
1222f76ee892STomi Valkeinen }
1223f76ee892STomi Valkeinen
1224f76ee892STomi Valkeinen r = sysfs_create_group(&dev->kobj, &dsicm_attr_group);
1225f76ee892STomi Valkeinen if (r) {
1226f76ee892STomi Valkeinen dev_err(dev, "failed to create sysfs files\n");
1227f76ee892STomi Valkeinen goto err_sysfs_create;
1228f76ee892STomi Valkeinen }
1229f76ee892STomi Valkeinen
1230f76ee892STomi Valkeinen return 0;
1231f76ee892STomi Valkeinen
1232f76ee892STomi Valkeinen err_sysfs_create:
1233f76ee892STomi Valkeinen if (bldev != NULL)
1234f76ee892STomi Valkeinen backlight_device_unregister(bldev);
1235f76ee892STomi Valkeinen err_reg:
1236f76ee892STomi Valkeinen return r;
1237f76ee892STomi Valkeinen }
1238f76ee892STomi Valkeinen
dsicm_remove(struct platform_device * pdev)1239da21ff39SUwe Kleine-König static void dsicm_remove(struct platform_device *pdev)
1240f76ee892STomi Valkeinen {
1241f76ee892STomi Valkeinen struct panel_drv_data *ddata = platform_get_drvdata(pdev);
1242f76ee892STomi Valkeinen struct omap_dss_device *dssdev = &ddata->dssdev;
1243f76ee892STomi Valkeinen struct backlight_device *bldev;
1244f76ee892STomi Valkeinen
1245f76ee892STomi Valkeinen dev_dbg(&pdev->dev, "remove\n");
1246f76ee892STomi Valkeinen
1247f76ee892STomi Valkeinen omapdss_unregister_display(dssdev);
1248f76ee892STomi Valkeinen
1249f76ee892STomi Valkeinen dsicm_disable(dssdev);
1250f76ee892STomi Valkeinen dsicm_disconnect(dssdev);
1251f76ee892STomi Valkeinen
1252f76ee892STomi Valkeinen sysfs_remove_group(&pdev->dev.kobj, &dsicm_attr_group);
1253f76ee892STomi Valkeinen
1254f76ee892STomi Valkeinen bldev = ddata->bldev;
1255f76ee892STomi Valkeinen if (bldev != NULL) {
1256f76ee892STomi Valkeinen bldev->props.power = FB_BLANK_POWERDOWN;
1257f76ee892STomi Valkeinen dsicm_bl_update_status(bldev);
1258f76ee892STomi Valkeinen backlight_device_unregister(bldev);
1259f76ee892STomi Valkeinen }
1260f76ee892STomi Valkeinen
1261f76ee892STomi Valkeinen omap_dss_put_device(ddata->in);
1262f76ee892STomi Valkeinen
1263f76ee892STomi Valkeinen dsicm_cancel_ulps_work(ddata);
1264f76ee892STomi Valkeinen
1265f76ee892STomi Valkeinen /* reset, to be sure that the panel is in a valid state */
1266f76ee892STomi Valkeinen dsicm_hw_reset(ddata);
1267f76ee892STomi Valkeinen }
1268f76ee892STomi Valkeinen
1269f76ee892STomi Valkeinen static const struct of_device_id dsicm_of_match[] = {
1270f76ee892STomi Valkeinen { .compatible = "omapdss,panel-dsi-cm", },
1271f76ee892STomi Valkeinen {},
1272f76ee892STomi Valkeinen };
1273f76ee892STomi Valkeinen
1274f76ee892STomi Valkeinen MODULE_DEVICE_TABLE(of, dsicm_of_match);
1275f76ee892STomi Valkeinen
1276f76ee892STomi Valkeinen static struct platform_driver dsicm_driver = {
1277f76ee892STomi Valkeinen .probe = dsicm_probe,
1278*01ecc142SUwe Kleine-König .remove = dsicm_remove,
1279f76ee892STomi Valkeinen .driver = {
1280f76ee892STomi Valkeinen .name = "panel-dsi-cm",
1281f76ee892STomi Valkeinen .of_match_table = dsicm_of_match,
1282f76ee892STomi Valkeinen },
1283f76ee892STomi Valkeinen };
1284f76ee892STomi Valkeinen
1285f76ee892STomi Valkeinen module_platform_driver(dsicm_driver);
1286f76ee892STomi Valkeinen
1287f76ee892STomi Valkeinen MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
1288f76ee892STomi Valkeinen MODULE_DESCRIPTION("Generic DSI Command Mode Panel Driver");
1289f76ee892STomi Valkeinen MODULE_LICENSE("GPL");
1290