xref: /linux/drivers/video/fbdev/omap2/omapfb/displays/panel-dsi-cm.c (revision 9066258d0a533530c2508f784e85c53b44f5d9e4)
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