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>
18*e8812acbSRob 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 {
359f76ee892STomi Valkeinen 	if (dev->props.fb_blank == FB_BLANK_UNBLANK &&
360f76ee892STomi Valkeinen 			dev->props.power == FB_BLANK_UNBLANK)
361f76ee892STomi Valkeinen 		return dev->props.brightness;
362f76ee892STomi Valkeinen 
363f76ee892STomi Valkeinen 	return 0;
364f76ee892STomi Valkeinen }
365f76ee892STomi Valkeinen 
366f76ee892STomi Valkeinen static const struct backlight_ops dsicm_bl_ops = {
367f76ee892STomi Valkeinen 	.get_brightness = dsicm_bl_get_intensity,
368f76ee892STomi Valkeinen 	.update_status  = dsicm_bl_update_status,
369f76ee892STomi Valkeinen };
370f76ee892STomi Valkeinen 
dsicm_get_resolution(struct omap_dss_device * dssdev,u16 * xres,u16 * yres)371f76ee892STomi Valkeinen static void dsicm_get_resolution(struct omap_dss_device *dssdev,
372f76ee892STomi Valkeinen 		u16 *xres, u16 *yres)
373f76ee892STomi Valkeinen {
374f76ee892STomi Valkeinen 	*xres = dssdev->panel.timings.x_res;
375f76ee892STomi Valkeinen 	*yres = dssdev->panel.timings.y_res;
376f76ee892STomi Valkeinen }
377f76ee892STomi Valkeinen 
dsicm_num_errors_show(struct device * dev,struct device_attribute * attr,char * buf)378f76ee892STomi Valkeinen static ssize_t dsicm_num_errors_show(struct device *dev,
379f76ee892STomi Valkeinen 		struct device_attribute *attr, char *buf)
380f76ee892STomi Valkeinen {
3816677b275SWolfram Sang 	struct panel_drv_data *ddata = dev_get_drvdata(dev);
382f76ee892STomi Valkeinen 	struct omap_dss_device *in = ddata->in;
383f76ee892STomi Valkeinen 	u8 errors = 0;
384f76ee892STomi Valkeinen 	int r;
385f76ee892STomi Valkeinen 
386f76ee892STomi Valkeinen 	mutex_lock(&ddata->lock);
387f76ee892STomi Valkeinen 
388f76ee892STomi Valkeinen 	if (ddata->enabled) {
389f76ee892STomi Valkeinen 		in->ops.dsi->bus_lock(in);
390f76ee892STomi Valkeinen 
391f76ee892STomi Valkeinen 		r = dsicm_wake_up(ddata);
392f76ee892STomi Valkeinen 		if (!r)
393f76ee892STomi Valkeinen 			r = dsicm_dcs_read_1(ddata, DCS_READ_NUM_ERRORS,
394f76ee892STomi Valkeinen 					&errors);
395f76ee892STomi Valkeinen 
396f76ee892STomi Valkeinen 		in->ops.dsi->bus_unlock(in);
397f76ee892STomi Valkeinen 	} else {
398f76ee892STomi Valkeinen 		r = -ENODEV;
399f76ee892STomi Valkeinen 	}
400f76ee892STomi Valkeinen 
401f76ee892STomi Valkeinen 	mutex_unlock(&ddata->lock);
402f76ee892STomi Valkeinen 
403f76ee892STomi Valkeinen 	if (r)
404f76ee892STomi Valkeinen 		return r;
405f76ee892STomi Valkeinen 
406f63658a5SJing Yao 	return sysfs_emit(buf, "%d\n", errors);
407f76ee892STomi Valkeinen }
408f76ee892STomi Valkeinen 
dsicm_hw_revision_show(struct device * dev,struct device_attribute * attr,char * buf)409f76ee892STomi Valkeinen static ssize_t dsicm_hw_revision_show(struct device *dev,
410f76ee892STomi Valkeinen 		struct device_attribute *attr, char *buf)
411f76ee892STomi Valkeinen {
4126677b275SWolfram Sang 	struct panel_drv_data *ddata = dev_get_drvdata(dev);
413f76ee892STomi Valkeinen 	struct omap_dss_device *in = ddata->in;
414f76ee892STomi Valkeinen 	u8 id1, id2, id3;
415f76ee892STomi Valkeinen 	int r;
416f76ee892STomi Valkeinen 
417f76ee892STomi Valkeinen 	mutex_lock(&ddata->lock);
418f76ee892STomi Valkeinen 
419f76ee892STomi Valkeinen 	if (ddata->enabled) {
420f76ee892STomi Valkeinen 		in->ops.dsi->bus_lock(in);
421f76ee892STomi Valkeinen 
422f76ee892STomi Valkeinen 		r = dsicm_wake_up(ddata);
423f76ee892STomi Valkeinen 		if (!r)
424f76ee892STomi Valkeinen 			r = dsicm_get_id(ddata, &id1, &id2, &id3);
425f76ee892STomi Valkeinen 
426f76ee892STomi Valkeinen 		in->ops.dsi->bus_unlock(in);
427f76ee892STomi Valkeinen 	} else {
428f76ee892STomi Valkeinen 		r = -ENODEV;
429f76ee892STomi Valkeinen 	}
430f76ee892STomi Valkeinen 
431f76ee892STomi Valkeinen 	mutex_unlock(&ddata->lock);
432f76ee892STomi Valkeinen 
433f76ee892STomi Valkeinen 	if (r)
434f76ee892STomi Valkeinen 		return r;
435f76ee892STomi Valkeinen 
436f63658a5SJing Yao 	return sysfs_emit(buf, "%02x.%02x.%02x\n", id1, id2, id3);
437f76ee892STomi Valkeinen }
438f76ee892STomi Valkeinen 
dsicm_store_ulps(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)439f76ee892STomi Valkeinen static ssize_t dsicm_store_ulps(struct device *dev,
440f76ee892STomi Valkeinen 		struct device_attribute *attr,
441f76ee892STomi Valkeinen 		const char *buf, size_t count)
442f76ee892STomi Valkeinen {
4436677b275SWolfram Sang 	struct panel_drv_data *ddata = dev_get_drvdata(dev);
444f76ee892STomi Valkeinen 	struct omap_dss_device *in = ddata->in;
445f76ee892STomi Valkeinen 	unsigned long t;
446f76ee892STomi Valkeinen 	int r;
447f76ee892STomi Valkeinen 
448f76ee892STomi Valkeinen 	r = kstrtoul(buf, 0, &t);
449f76ee892STomi Valkeinen 	if (r)
450f76ee892STomi Valkeinen 		return r;
451f76ee892STomi Valkeinen 
452f76ee892STomi Valkeinen 	mutex_lock(&ddata->lock);
453f76ee892STomi Valkeinen 
454f76ee892STomi Valkeinen 	if (ddata->enabled) {
455f76ee892STomi Valkeinen 		in->ops.dsi->bus_lock(in);
456f76ee892STomi Valkeinen 
457f76ee892STomi Valkeinen 		if (t)
458f76ee892STomi Valkeinen 			r = dsicm_enter_ulps(ddata);
459f76ee892STomi Valkeinen 		else
460f76ee892STomi Valkeinen 			r = dsicm_wake_up(ddata);
461f76ee892STomi Valkeinen 
462f76ee892STomi Valkeinen 		in->ops.dsi->bus_unlock(in);
463f76ee892STomi Valkeinen 	}
464f76ee892STomi Valkeinen 
465f76ee892STomi Valkeinen 	mutex_unlock(&ddata->lock);
466f76ee892STomi Valkeinen 
467f76ee892STomi Valkeinen 	if (r)
468f76ee892STomi Valkeinen 		return r;
469f76ee892STomi Valkeinen 
470f76ee892STomi Valkeinen 	return count;
471f76ee892STomi Valkeinen }
472f76ee892STomi Valkeinen 
dsicm_show_ulps(struct device * dev,struct device_attribute * attr,char * buf)473f76ee892STomi Valkeinen static ssize_t dsicm_show_ulps(struct device *dev,
474f76ee892STomi Valkeinen 		struct device_attribute *attr,
475f76ee892STomi Valkeinen 		char *buf)
476f76ee892STomi Valkeinen {
4776677b275SWolfram Sang 	struct panel_drv_data *ddata = dev_get_drvdata(dev);
478f76ee892STomi Valkeinen 	unsigned t;
479f76ee892STomi Valkeinen 
480f76ee892STomi Valkeinen 	mutex_lock(&ddata->lock);
481f76ee892STomi Valkeinen 	t = ddata->ulps_enabled;
482f76ee892STomi Valkeinen 	mutex_unlock(&ddata->lock);
483f76ee892STomi Valkeinen 
484f63658a5SJing Yao 	return sysfs_emit(buf, "%u\n", t);
485f76ee892STomi Valkeinen }
486f76ee892STomi Valkeinen 
dsicm_store_ulps_timeout(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)487f76ee892STomi Valkeinen static ssize_t dsicm_store_ulps_timeout(struct device *dev,
488f76ee892STomi Valkeinen 		struct device_attribute *attr,
489f76ee892STomi Valkeinen 		const char *buf, size_t count)
490f76ee892STomi Valkeinen {
4916677b275SWolfram Sang 	struct panel_drv_data *ddata = dev_get_drvdata(dev);
492f76ee892STomi Valkeinen 	struct omap_dss_device *in = ddata->in;
493f76ee892STomi Valkeinen 	unsigned long t;
494f76ee892STomi Valkeinen 	int r;
495f76ee892STomi Valkeinen 
496f76ee892STomi Valkeinen 	r = kstrtoul(buf, 0, &t);
497f76ee892STomi Valkeinen 	if (r)
498f76ee892STomi Valkeinen 		return r;
499f76ee892STomi Valkeinen 
500f76ee892STomi Valkeinen 	mutex_lock(&ddata->lock);
501f76ee892STomi Valkeinen 	ddata->ulps_timeout = t;
502f76ee892STomi Valkeinen 
503f76ee892STomi Valkeinen 	if (ddata->enabled) {
504f76ee892STomi Valkeinen 		/* dsicm_wake_up will restart the timer */
505f76ee892STomi Valkeinen 		in->ops.dsi->bus_lock(in);
506f76ee892STomi Valkeinen 		r = dsicm_wake_up(ddata);
507f76ee892STomi Valkeinen 		in->ops.dsi->bus_unlock(in);
508f76ee892STomi Valkeinen 	}
509f76ee892STomi Valkeinen 
510f76ee892STomi Valkeinen 	mutex_unlock(&ddata->lock);
511f76ee892STomi Valkeinen 
512f76ee892STomi Valkeinen 	if (r)
513f76ee892STomi Valkeinen 		return r;
514f76ee892STomi Valkeinen 
515f76ee892STomi Valkeinen 	return count;
516f76ee892STomi Valkeinen }
517f76ee892STomi Valkeinen 
dsicm_show_ulps_timeout(struct device * dev,struct device_attribute * attr,char * buf)518f76ee892STomi Valkeinen static ssize_t dsicm_show_ulps_timeout(struct device *dev,
519f76ee892STomi Valkeinen 		struct device_attribute *attr,
520f76ee892STomi Valkeinen 		char *buf)
521f76ee892STomi Valkeinen {
5226677b275SWolfram Sang 	struct panel_drv_data *ddata = dev_get_drvdata(dev);
523f76ee892STomi Valkeinen 	unsigned t;
524f76ee892STomi Valkeinen 
525f76ee892STomi Valkeinen 	mutex_lock(&ddata->lock);
526f76ee892STomi Valkeinen 	t = ddata->ulps_timeout;
527f76ee892STomi Valkeinen 	mutex_unlock(&ddata->lock);
528f76ee892STomi Valkeinen 
529f63658a5SJing Yao 	return sysfs_emit(buf, "%u\n", t);
530f76ee892STomi Valkeinen }
531f76ee892STomi Valkeinen 
532f76ee892STomi Valkeinen static DEVICE_ATTR(num_dsi_errors, S_IRUGO, dsicm_num_errors_show, NULL);
533f76ee892STomi Valkeinen static DEVICE_ATTR(hw_revision, S_IRUGO, dsicm_hw_revision_show, NULL);
534f76ee892STomi Valkeinen static DEVICE_ATTR(ulps, S_IRUGO | S_IWUSR,
535f76ee892STomi Valkeinen 		dsicm_show_ulps, dsicm_store_ulps);
536f76ee892STomi Valkeinen static DEVICE_ATTR(ulps_timeout, S_IRUGO | S_IWUSR,
537f76ee892STomi Valkeinen 		dsicm_show_ulps_timeout, dsicm_store_ulps_timeout);
538f76ee892STomi Valkeinen 
539f76ee892STomi Valkeinen static struct attribute *dsicm_attrs[] = {
540f76ee892STomi Valkeinen 	&dev_attr_num_dsi_errors.attr,
541f76ee892STomi Valkeinen 	&dev_attr_hw_revision.attr,
542f76ee892STomi Valkeinen 	&dev_attr_ulps.attr,
543f76ee892STomi Valkeinen 	&dev_attr_ulps_timeout.attr,
544f76ee892STomi Valkeinen 	NULL,
545f76ee892STomi Valkeinen };
546f76ee892STomi Valkeinen 
5475ffe80c1SArvind Yadav static const struct attribute_group dsicm_attr_group = {
548f76ee892STomi Valkeinen 	.attrs = dsicm_attrs,
549f76ee892STomi Valkeinen };
550f76ee892STomi Valkeinen 
dsicm_hw_reset(struct panel_drv_data * ddata)551f76ee892STomi Valkeinen static void dsicm_hw_reset(struct panel_drv_data *ddata)
552f76ee892STomi Valkeinen {
55367c366deSDmitry Torokhov 	/*
55467c366deSDmitry Torokhov 	 * Note that we appear to activate the reset line here. However
55567c366deSDmitry Torokhov 	 * existing DTSes specified incorrect polarity for it (active high),
55667c366deSDmitry Torokhov 	 * so in fact this deasserts the reset line.
55767c366deSDmitry Torokhov 	 */
55867c366deSDmitry Torokhov 	gpiod_set_value_cansleep(ddata->reset_gpio, 1);
559f76ee892STomi Valkeinen 	udelay(10);
560f76ee892STomi Valkeinen 	/* reset the panel */
56167c366deSDmitry Torokhov 	gpiod_set_value_cansleep(ddata->reset_gpio, 0);
56267c366deSDmitry Torokhov 	/* keep reset asserted */
563f76ee892STomi Valkeinen 	udelay(10);
56467c366deSDmitry Torokhov 	/* release reset line */
56567c366deSDmitry Torokhov 	gpiod_set_value_cansleep(ddata->reset_gpio, 1);
566f76ee892STomi Valkeinen 	/* wait after releasing reset */
567f76ee892STomi Valkeinen 	usleep_range(5000, 10000);
568f76ee892STomi Valkeinen }
569f76ee892STomi Valkeinen 
dsicm_power_on(struct panel_drv_data * ddata)570f76ee892STomi Valkeinen static int dsicm_power_on(struct panel_drv_data *ddata)
571f76ee892STomi Valkeinen {
572f76ee892STomi Valkeinen 	struct omap_dss_device *in = ddata->in;
573f76ee892STomi Valkeinen 	u8 id1, id2, id3;
574f76ee892STomi Valkeinen 	int r;
575f76ee892STomi Valkeinen 	struct omap_dss_dsi_config dsi_config = {
576f76ee892STomi Valkeinen 		.mode = OMAP_DSS_DSI_CMD_MODE,
577f76ee892STomi Valkeinen 		.pixel_format = OMAP_DSS_DSI_FMT_RGB888,
578f76ee892STomi Valkeinen 		.timings = &ddata->timings,
579f76ee892STomi Valkeinen 		.hs_clk_min = 150000000,
580f76ee892STomi Valkeinen 		.hs_clk_max = 300000000,
581f76ee892STomi Valkeinen 		.lp_clk_min = 7000000,
582f76ee892STomi Valkeinen 		.lp_clk_max = 10000000,
583f76ee892STomi Valkeinen 	};
584f76ee892STomi Valkeinen 
585f76ee892STomi Valkeinen 	if (ddata->pin_config.num_pins > 0) {
586f76ee892STomi Valkeinen 		r = in->ops.dsi->configure_pins(in, &ddata->pin_config);
587f76ee892STomi Valkeinen 		if (r) {
588f76ee892STomi Valkeinen 			dev_err(&ddata->pdev->dev,
589f76ee892STomi Valkeinen 				"failed to configure DSI pins\n");
590f76ee892STomi Valkeinen 			goto err0;
591f76ee892STomi Valkeinen 		}
592f76ee892STomi Valkeinen 	}
593f76ee892STomi Valkeinen 
594f76ee892STomi Valkeinen 	r = in->ops.dsi->set_config(in, &dsi_config);
595f76ee892STomi Valkeinen 	if (r) {
596f76ee892STomi Valkeinen 		dev_err(&ddata->pdev->dev, "failed to configure DSI\n");
597f76ee892STomi Valkeinen 		goto err0;
598f76ee892STomi Valkeinen 	}
599f76ee892STomi Valkeinen 
600f76ee892STomi Valkeinen 	r = in->ops.dsi->enable(in);
601f76ee892STomi Valkeinen 	if (r) {
602f76ee892STomi Valkeinen 		dev_err(&ddata->pdev->dev, "failed to enable DSI\n");
603f76ee892STomi Valkeinen 		goto err0;
604f76ee892STomi Valkeinen 	}
605f76ee892STomi Valkeinen 
606f76ee892STomi Valkeinen 	dsicm_hw_reset(ddata);
607f76ee892STomi Valkeinen 
608f76ee892STomi Valkeinen 	in->ops.dsi->enable_hs(in, ddata->channel, false);
609f76ee892STomi Valkeinen 
610f76ee892STomi Valkeinen 	r = dsicm_sleep_out(ddata);
611f76ee892STomi Valkeinen 	if (r)
612f76ee892STomi Valkeinen 		goto err;
613f76ee892STomi Valkeinen 
614f76ee892STomi Valkeinen 	r = dsicm_get_id(ddata, &id1, &id2, &id3);
615f76ee892STomi Valkeinen 	if (r)
616f76ee892STomi Valkeinen 		goto err;
617f76ee892STomi Valkeinen 
618f76ee892STomi Valkeinen 	r = dsicm_dcs_write_1(ddata, DCS_BRIGHTNESS, 0xff);
619f76ee892STomi Valkeinen 	if (r)
620f76ee892STomi Valkeinen 		goto err;
621f76ee892STomi Valkeinen 
622f76ee892STomi Valkeinen 	r = dsicm_dcs_write_1(ddata, DCS_CTRL_DISPLAY,
623f76ee892STomi Valkeinen 			(1<<2) | (1<<5));	/* BL | BCTRL */
624f76ee892STomi Valkeinen 	if (r)
625f76ee892STomi Valkeinen 		goto err;
626f76ee892STomi Valkeinen 
627f76ee892STomi Valkeinen 	r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_PIXEL_FORMAT,
628f76ee892STomi Valkeinen 		MIPI_DCS_PIXEL_FMT_24BIT);
629f76ee892STomi Valkeinen 	if (r)
630f76ee892STomi Valkeinen 		goto err;
631f76ee892STomi Valkeinen 
632f76ee892STomi Valkeinen 	r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_DISPLAY_ON);
633f76ee892STomi Valkeinen 	if (r)
634f76ee892STomi Valkeinen 		goto err;
635f76ee892STomi Valkeinen 
636f76ee892STomi Valkeinen 	r = _dsicm_enable_te(ddata, ddata->te_enabled);
637f76ee892STomi Valkeinen 	if (r)
638f76ee892STomi Valkeinen 		goto err;
639f76ee892STomi Valkeinen 
640f76ee892STomi Valkeinen 	r = in->ops.dsi->enable_video_output(in, ddata->channel);
641f76ee892STomi Valkeinen 	if (r)
642f76ee892STomi Valkeinen 		goto err;
643f76ee892STomi Valkeinen 
644f76ee892STomi Valkeinen 	ddata->enabled = 1;
645f76ee892STomi Valkeinen 
646f76ee892STomi Valkeinen 	if (!ddata->intro_printed) {
647f76ee892STomi Valkeinen 		dev_info(&ddata->pdev->dev, "panel revision %02x.%02x.%02x\n",
648f76ee892STomi Valkeinen 			id1, id2, id3);
649f76ee892STomi Valkeinen 		ddata->intro_printed = true;
650f76ee892STomi Valkeinen 	}
651f76ee892STomi Valkeinen 
652f76ee892STomi Valkeinen 	in->ops.dsi->enable_hs(in, ddata->channel, true);
653f76ee892STomi Valkeinen 
654f76ee892STomi Valkeinen 	return 0;
655f76ee892STomi Valkeinen err:
656f76ee892STomi Valkeinen 	dev_err(&ddata->pdev->dev, "error while enabling panel, issuing HW reset\n");
657f76ee892STomi Valkeinen 
658f76ee892STomi Valkeinen 	dsicm_hw_reset(ddata);
659f76ee892STomi Valkeinen 
660f76ee892STomi Valkeinen 	in->ops.dsi->disable(in, true, false);
661f76ee892STomi Valkeinen err0:
662f76ee892STomi Valkeinen 	return r;
663f76ee892STomi Valkeinen }
664f76ee892STomi Valkeinen 
dsicm_power_off(struct panel_drv_data * ddata)665f76ee892STomi Valkeinen static void dsicm_power_off(struct panel_drv_data *ddata)
666f76ee892STomi Valkeinen {
667f76ee892STomi Valkeinen 	struct omap_dss_device *in = ddata->in;
668f76ee892STomi Valkeinen 	int r;
669f76ee892STomi Valkeinen 
670f76ee892STomi Valkeinen 	in->ops.dsi->disable_video_output(in, ddata->channel);
671f76ee892STomi Valkeinen 
672f76ee892STomi Valkeinen 	r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_DISPLAY_OFF);
673f76ee892STomi Valkeinen 	if (!r)
674f76ee892STomi Valkeinen 		r = dsicm_sleep_in(ddata);
675f76ee892STomi Valkeinen 
676f76ee892STomi Valkeinen 	if (r) {
677f76ee892STomi Valkeinen 		dev_err(&ddata->pdev->dev,
678f76ee892STomi Valkeinen 				"error disabling panel, issuing HW reset\n");
679f76ee892STomi Valkeinen 		dsicm_hw_reset(ddata);
680f76ee892STomi Valkeinen 	}
681f76ee892STomi Valkeinen 
682f76ee892STomi Valkeinen 	in->ops.dsi->disable(in, true, false);
683f76ee892STomi Valkeinen 
684f76ee892STomi Valkeinen 	ddata->enabled = 0;
685f76ee892STomi Valkeinen }
686f76ee892STomi Valkeinen 
dsicm_panel_reset(struct panel_drv_data * ddata)687f76ee892STomi Valkeinen static int dsicm_panel_reset(struct panel_drv_data *ddata)
688f76ee892STomi Valkeinen {
689f76ee892STomi Valkeinen 	dev_err(&ddata->pdev->dev, "performing LCD reset\n");
690f76ee892STomi Valkeinen 
691f76ee892STomi Valkeinen 	dsicm_power_off(ddata);
692f76ee892STomi Valkeinen 	dsicm_hw_reset(ddata);
693f76ee892STomi Valkeinen 	return dsicm_power_on(ddata);
694f76ee892STomi Valkeinen }
695f76ee892STomi Valkeinen 
dsicm_connect(struct omap_dss_device * dssdev)696f76ee892STomi Valkeinen static int dsicm_connect(struct omap_dss_device *dssdev)
697f76ee892STomi Valkeinen {
698f76ee892STomi Valkeinen 	struct panel_drv_data *ddata = to_panel_data(dssdev);
699f76ee892STomi Valkeinen 	struct omap_dss_device *in = ddata->in;
700f76ee892STomi Valkeinen 	struct device *dev = &ddata->pdev->dev;
701f76ee892STomi Valkeinen 	int r;
702f76ee892STomi Valkeinen 
703f76ee892STomi Valkeinen 	if (omapdss_device_is_connected(dssdev))
704f76ee892STomi Valkeinen 		return 0;
705f76ee892STomi Valkeinen 
706f76ee892STomi Valkeinen 	r = in->ops.dsi->connect(in, dssdev);
707f76ee892STomi Valkeinen 	if (r) {
708f76ee892STomi Valkeinen 		dev_err(dev, "Failed to connect to video source\n");
709f76ee892STomi Valkeinen 		return r;
710f76ee892STomi Valkeinen 	}
711f76ee892STomi Valkeinen 
712f76ee892STomi Valkeinen 	r = in->ops.dsi->request_vc(ddata->in, &ddata->channel);
713f76ee892STomi Valkeinen 	if (r) {
714f76ee892STomi Valkeinen 		dev_err(dev, "failed to get virtual channel\n");
715f76ee892STomi Valkeinen 		goto err_req_vc;
716f76ee892STomi Valkeinen 	}
717f76ee892STomi Valkeinen 
718f76ee892STomi Valkeinen 	r = in->ops.dsi->set_vc_id(ddata->in, ddata->channel, TCH);
719f76ee892STomi Valkeinen 	if (r) {
720f76ee892STomi Valkeinen 		dev_err(dev, "failed to set VC_ID\n");
721f76ee892STomi Valkeinen 		goto err_vc_id;
722f76ee892STomi Valkeinen 	}
723f76ee892STomi Valkeinen 
724f76ee892STomi Valkeinen 	return 0;
725f76ee892STomi Valkeinen 
726f76ee892STomi Valkeinen err_vc_id:
727f76ee892STomi Valkeinen 	in->ops.dsi->release_vc(ddata->in, ddata->channel);
728f76ee892STomi Valkeinen err_req_vc:
729f76ee892STomi Valkeinen 	in->ops.dsi->disconnect(in, dssdev);
730f76ee892STomi Valkeinen 	return r;
731f76ee892STomi Valkeinen }
732f76ee892STomi Valkeinen 
dsicm_disconnect(struct omap_dss_device * dssdev)733f76ee892STomi Valkeinen static void dsicm_disconnect(struct omap_dss_device *dssdev)
734f76ee892STomi Valkeinen {
735f76ee892STomi Valkeinen 	struct panel_drv_data *ddata = to_panel_data(dssdev);
736f76ee892STomi Valkeinen 	struct omap_dss_device *in = ddata->in;
737f76ee892STomi Valkeinen 
738f76ee892STomi Valkeinen 	if (!omapdss_device_is_connected(dssdev))
739f76ee892STomi Valkeinen 		return;
740f76ee892STomi Valkeinen 
741f76ee892STomi Valkeinen 	in->ops.dsi->release_vc(in, ddata->channel);
742f76ee892STomi Valkeinen 	in->ops.dsi->disconnect(in, dssdev);
743f76ee892STomi Valkeinen }
744f76ee892STomi Valkeinen 
dsicm_enable(struct omap_dss_device * dssdev)745f76ee892STomi Valkeinen static int dsicm_enable(struct omap_dss_device *dssdev)
746f76ee892STomi Valkeinen {
747f76ee892STomi Valkeinen 	struct panel_drv_data *ddata = to_panel_data(dssdev);
748f76ee892STomi Valkeinen 	struct omap_dss_device *in = ddata->in;
749f76ee892STomi Valkeinen 	int r;
750f76ee892STomi Valkeinen 
751f76ee892STomi Valkeinen 	dev_dbg(&ddata->pdev->dev, "enable\n");
752f76ee892STomi Valkeinen 
753f76ee892STomi Valkeinen 	mutex_lock(&ddata->lock);
754f76ee892STomi Valkeinen 
755f76ee892STomi Valkeinen 	if (!omapdss_device_is_connected(dssdev)) {
756f76ee892STomi Valkeinen 		r = -ENODEV;
757f76ee892STomi Valkeinen 		goto err;
758f76ee892STomi Valkeinen 	}
759f76ee892STomi Valkeinen 
760f76ee892STomi Valkeinen 	if (omapdss_device_is_enabled(dssdev)) {
761f76ee892STomi Valkeinen 		r = 0;
762f76ee892STomi Valkeinen 		goto err;
763f76ee892STomi Valkeinen 	}
764f76ee892STomi Valkeinen 
765f76ee892STomi Valkeinen 	in->ops.dsi->bus_lock(in);
766f76ee892STomi Valkeinen 
767f76ee892STomi Valkeinen 	r = dsicm_power_on(ddata);
768f76ee892STomi Valkeinen 
769f76ee892STomi Valkeinen 	in->ops.dsi->bus_unlock(in);
770f76ee892STomi Valkeinen 
771f76ee892STomi Valkeinen 	if (r)
772f76ee892STomi Valkeinen 		goto err;
773f76ee892STomi Valkeinen 
774f76ee892STomi Valkeinen 	dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
775f76ee892STomi Valkeinen 
776f76ee892STomi Valkeinen 	mutex_unlock(&ddata->lock);
777f76ee892STomi Valkeinen 
778f76ee892STomi Valkeinen 	return 0;
779f76ee892STomi Valkeinen err:
780f76ee892STomi Valkeinen 	dev_dbg(&ddata->pdev->dev, "enable failed\n");
781f76ee892STomi Valkeinen 	mutex_unlock(&ddata->lock);
782f76ee892STomi Valkeinen 	return r;
783f76ee892STomi Valkeinen }
784f76ee892STomi Valkeinen 
dsicm_disable(struct omap_dss_device * dssdev)785f76ee892STomi Valkeinen static void dsicm_disable(struct omap_dss_device *dssdev)
786f76ee892STomi Valkeinen {
787f76ee892STomi Valkeinen 	struct panel_drv_data *ddata = to_panel_data(dssdev);
788f76ee892STomi Valkeinen 	struct omap_dss_device *in = ddata->in;
789f76ee892STomi Valkeinen 	int r;
790f76ee892STomi Valkeinen 
791f76ee892STomi Valkeinen 	dev_dbg(&ddata->pdev->dev, "disable\n");
792f76ee892STomi Valkeinen 
793f76ee892STomi Valkeinen 	mutex_lock(&ddata->lock);
794f76ee892STomi Valkeinen 
795f76ee892STomi Valkeinen 	dsicm_cancel_ulps_work(ddata);
796f76ee892STomi Valkeinen 
797f76ee892STomi Valkeinen 	in->ops.dsi->bus_lock(in);
798f76ee892STomi Valkeinen 
799f76ee892STomi Valkeinen 	if (omapdss_device_is_enabled(dssdev)) {
800f76ee892STomi Valkeinen 		r = dsicm_wake_up(ddata);
801f76ee892STomi Valkeinen 		if (!r)
802f76ee892STomi Valkeinen 			dsicm_power_off(ddata);
803f76ee892STomi Valkeinen 	}
804f76ee892STomi Valkeinen 
805f76ee892STomi Valkeinen 	in->ops.dsi->bus_unlock(in);
806f76ee892STomi Valkeinen 
807f76ee892STomi Valkeinen 	dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
808f76ee892STomi Valkeinen 
809f76ee892STomi Valkeinen 	mutex_unlock(&ddata->lock);
810f76ee892STomi Valkeinen }
811f76ee892STomi Valkeinen 
dsicm_framedone_cb(int err,void * data)812f76ee892STomi Valkeinen static void dsicm_framedone_cb(int err, void *data)
813f76ee892STomi Valkeinen {
814f76ee892STomi Valkeinen 	struct panel_drv_data *ddata = data;
815f76ee892STomi Valkeinen 	struct omap_dss_device *in = ddata->in;
816f76ee892STomi Valkeinen 
817f76ee892STomi Valkeinen 	dev_dbg(&ddata->pdev->dev, "framedone, err %d\n", err);
818f76ee892STomi Valkeinen 	in->ops.dsi->bus_unlock(ddata->in);
819f76ee892STomi Valkeinen }
820f76ee892STomi Valkeinen 
dsicm_te_isr(int irq,void * data)821f76ee892STomi Valkeinen static irqreturn_t dsicm_te_isr(int irq, void *data)
822f76ee892STomi Valkeinen {
823f76ee892STomi Valkeinen 	struct panel_drv_data *ddata = data;
824f76ee892STomi Valkeinen 	struct omap_dss_device *in = ddata->in;
825f76ee892STomi Valkeinen 	int old;
826f76ee892STomi Valkeinen 	int r;
827f76ee892STomi Valkeinen 
828f76ee892STomi Valkeinen 	old = atomic_cmpxchg(&ddata->do_update, 1, 0);
829f76ee892STomi Valkeinen 
830f76ee892STomi Valkeinen 	if (old) {
831f76ee892STomi Valkeinen 		cancel_delayed_work(&ddata->te_timeout_work);
832f76ee892STomi Valkeinen 
833f76ee892STomi Valkeinen 		r = in->ops.dsi->update(in, ddata->channel, dsicm_framedone_cb,
834f76ee892STomi Valkeinen 				ddata);
835f76ee892STomi Valkeinen 		if (r)
836f76ee892STomi Valkeinen 			goto err;
837f76ee892STomi Valkeinen 	}
838f76ee892STomi Valkeinen 
839f76ee892STomi Valkeinen 	return IRQ_HANDLED;
840f76ee892STomi Valkeinen err:
841f76ee892STomi Valkeinen 	dev_err(&ddata->pdev->dev, "start update failed\n");
842f76ee892STomi Valkeinen 	in->ops.dsi->bus_unlock(in);
843f76ee892STomi Valkeinen 	return IRQ_HANDLED;
844f76ee892STomi Valkeinen }
845f76ee892STomi Valkeinen 
dsicm_te_timeout_work_callback(struct work_struct * work)846f76ee892STomi Valkeinen static void dsicm_te_timeout_work_callback(struct work_struct *work)
847f76ee892STomi Valkeinen {
848f76ee892STomi Valkeinen 	struct panel_drv_data *ddata = container_of(work, struct panel_drv_data,
849f76ee892STomi Valkeinen 					te_timeout_work.work);
850f76ee892STomi Valkeinen 	struct omap_dss_device *in = ddata->in;
851f76ee892STomi Valkeinen 
852f76ee892STomi Valkeinen 	dev_err(&ddata->pdev->dev, "TE not received for 250ms!\n");
853f76ee892STomi Valkeinen 
854f76ee892STomi Valkeinen 	atomic_set(&ddata->do_update, 0);
855f76ee892STomi Valkeinen 	in->ops.dsi->bus_unlock(in);
856f76ee892STomi Valkeinen }
857f76ee892STomi Valkeinen 
dsicm_update(struct omap_dss_device * dssdev,u16 x,u16 y,u16 w,u16 h)858f76ee892STomi Valkeinen static int dsicm_update(struct omap_dss_device *dssdev,
859f76ee892STomi Valkeinen 				    u16 x, u16 y, u16 w, u16 h)
860f76ee892STomi Valkeinen {
861f76ee892STomi Valkeinen 	struct panel_drv_data *ddata = to_panel_data(dssdev);
862f76ee892STomi Valkeinen 	struct omap_dss_device *in = ddata->in;
863f76ee892STomi Valkeinen 	int r;
864f76ee892STomi Valkeinen 
865f76ee892STomi Valkeinen 	dev_dbg(&ddata->pdev->dev, "update %d, %d, %d x %d\n", x, y, w, h);
866f76ee892STomi Valkeinen 
867f76ee892STomi Valkeinen 	mutex_lock(&ddata->lock);
868f76ee892STomi Valkeinen 	in->ops.dsi->bus_lock(in);
869f76ee892STomi Valkeinen 
870f76ee892STomi Valkeinen 	r = dsicm_wake_up(ddata);
871f76ee892STomi Valkeinen 	if (r)
872f76ee892STomi Valkeinen 		goto err;
873f76ee892STomi Valkeinen 
874f76ee892STomi Valkeinen 	if (!ddata->enabled) {
875f76ee892STomi Valkeinen 		r = 0;
876f76ee892STomi Valkeinen 		goto err;
877f76ee892STomi Valkeinen 	}
878f76ee892STomi Valkeinen 
879f76ee892STomi Valkeinen 	/* XXX no need to send this every frame, but dsi break if not done */
880f76ee892STomi Valkeinen 	r = dsicm_set_update_window(ddata, 0, 0,
881f76ee892STomi Valkeinen 			dssdev->panel.timings.x_res,
882f76ee892STomi Valkeinen 			dssdev->panel.timings.y_res);
883f76ee892STomi Valkeinen 	if (r)
884f76ee892STomi Valkeinen 		goto err;
885f76ee892STomi Valkeinen 
88667c366deSDmitry Torokhov 	if (ddata->te_enabled && ddata->ext_te_gpio) {
887f76ee892STomi Valkeinen 		schedule_delayed_work(&ddata->te_timeout_work,
888f76ee892STomi Valkeinen 				msecs_to_jiffies(250));
889f76ee892STomi Valkeinen 		atomic_set(&ddata->do_update, 1);
890f76ee892STomi Valkeinen 	} else {
891f76ee892STomi Valkeinen 		r = in->ops.dsi->update(in, ddata->channel, dsicm_framedone_cb,
892f76ee892STomi Valkeinen 				ddata);
893f76ee892STomi Valkeinen 		if (r)
894f76ee892STomi Valkeinen 			goto err;
895f76ee892STomi Valkeinen 	}
896f76ee892STomi Valkeinen 
897f76ee892STomi Valkeinen 	/* note: no bus_unlock here. unlock is in framedone_cb */
898f76ee892STomi Valkeinen 	mutex_unlock(&ddata->lock);
899f76ee892STomi Valkeinen 	return 0;
900f76ee892STomi Valkeinen err:
901f76ee892STomi Valkeinen 	in->ops.dsi->bus_unlock(in);
902f76ee892STomi Valkeinen 	mutex_unlock(&ddata->lock);
903f76ee892STomi Valkeinen 	return r;
904f76ee892STomi Valkeinen }
905f76ee892STomi Valkeinen 
dsicm_sync(struct omap_dss_device * dssdev)906f76ee892STomi Valkeinen static int dsicm_sync(struct omap_dss_device *dssdev)
907f76ee892STomi Valkeinen {
908f76ee892STomi Valkeinen 	struct panel_drv_data *ddata = to_panel_data(dssdev);
909f76ee892STomi Valkeinen 	struct omap_dss_device *in = ddata->in;
910f76ee892STomi Valkeinen 
911f76ee892STomi Valkeinen 	dev_dbg(&ddata->pdev->dev, "sync\n");
912f76ee892STomi Valkeinen 
913f76ee892STomi Valkeinen 	mutex_lock(&ddata->lock);
914f76ee892STomi Valkeinen 	in->ops.dsi->bus_lock(in);
915f76ee892STomi Valkeinen 	in->ops.dsi->bus_unlock(in);
916f76ee892STomi Valkeinen 	mutex_unlock(&ddata->lock);
917f76ee892STomi Valkeinen 
918f76ee892STomi Valkeinen 	dev_dbg(&ddata->pdev->dev, "sync done\n");
919f76ee892STomi Valkeinen 
920f76ee892STomi Valkeinen 	return 0;
921f76ee892STomi Valkeinen }
922f76ee892STomi Valkeinen 
_dsicm_enable_te(struct panel_drv_data * ddata,bool enable)923f76ee892STomi Valkeinen static int _dsicm_enable_te(struct panel_drv_data *ddata, bool enable)
924f76ee892STomi Valkeinen {
925f76ee892STomi Valkeinen 	struct omap_dss_device *in = ddata->in;
926f76ee892STomi Valkeinen 	int r;
927f76ee892STomi Valkeinen 
928f76ee892STomi Valkeinen 	if (enable)
929f76ee892STomi Valkeinen 		r = dsicm_dcs_write_1(ddata, MIPI_DCS_SET_TEAR_ON, 0);
930f76ee892STomi Valkeinen 	else
931f76ee892STomi Valkeinen 		r = dsicm_dcs_write_0(ddata, MIPI_DCS_SET_TEAR_OFF);
932f76ee892STomi Valkeinen 
93367c366deSDmitry Torokhov 	if (!ddata->ext_te_gpio)
934f76ee892STomi Valkeinen 		in->ops.dsi->enable_te(in, enable);
935f76ee892STomi Valkeinen 
936f76ee892STomi Valkeinen 	/* possible panel bug */
937f76ee892STomi Valkeinen 	msleep(100);
938f76ee892STomi Valkeinen 
939f76ee892STomi Valkeinen 	return r;
940f76ee892STomi Valkeinen }
941f76ee892STomi Valkeinen 
dsicm_enable_te(struct omap_dss_device * dssdev,bool enable)942f76ee892STomi Valkeinen static int dsicm_enable_te(struct omap_dss_device *dssdev, bool enable)
943f76ee892STomi Valkeinen {
944f76ee892STomi Valkeinen 	struct panel_drv_data *ddata = to_panel_data(dssdev);
945f76ee892STomi Valkeinen 	struct omap_dss_device *in = ddata->in;
946f76ee892STomi Valkeinen 	int r;
947f76ee892STomi Valkeinen 
948f76ee892STomi Valkeinen 	mutex_lock(&ddata->lock);
949f76ee892STomi Valkeinen 
950f76ee892STomi Valkeinen 	if (ddata->te_enabled == enable)
951f76ee892STomi Valkeinen 		goto end;
952f76ee892STomi Valkeinen 
953f76ee892STomi Valkeinen 	in->ops.dsi->bus_lock(in);
954f76ee892STomi Valkeinen 
955f76ee892STomi Valkeinen 	if (ddata->enabled) {
956f76ee892STomi Valkeinen 		r = dsicm_wake_up(ddata);
957f76ee892STomi Valkeinen 		if (r)
958f76ee892STomi Valkeinen 			goto err;
959f76ee892STomi Valkeinen 
960f76ee892STomi Valkeinen 		r = _dsicm_enable_te(ddata, enable);
961f76ee892STomi Valkeinen 		if (r)
962f76ee892STomi Valkeinen 			goto err;
963f76ee892STomi Valkeinen 	}
964f76ee892STomi Valkeinen 
965f76ee892STomi Valkeinen 	ddata->te_enabled = enable;
966f76ee892STomi Valkeinen 
967f76ee892STomi Valkeinen 	in->ops.dsi->bus_unlock(in);
968f76ee892STomi Valkeinen end:
969f76ee892STomi Valkeinen 	mutex_unlock(&ddata->lock);
970f76ee892STomi Valkeinen 
971f76ee892STomi Valkeinen 	return 0;
972f76ee892STomi Valkeinen err:
973f76ee892STomi Valkeinen 	in->ops.dsi->bus_unlock(in);
974f76ee892STomi Valkeinen 	mutex_unlock(&ddata->lock);
975f76ee892STomi Valkeinen 
976f76ee892STomi Valkeinen 	return r;
977f76ee892STomi Valkeinen }
978f76ee892STomi Valkeinen 
dsicm_get_te(struct omap_dss_device * dssdev)979f76ee892STomi Valkeinen static int dsicm_get_te(struct omap_dss_device *dssdev)
980f76ee892STomi Valkeinen {
981f76ee892STomi Valkeinen 	struct panel_drv_data *ddata = to_panel_data(dssdev);
982f76ee892STomi Valkeinen 	int r;
983f76ee892STomi Valkeinen 
984f76ee892STomi Valkeinen 	mutex_lock(&ddata->lock);
985f76ee892STomi Valkeinen 	r = ddata->te_enabled;
986f76ee892STomi Valkeinen 	mutex_unlock(&ddata->lock);
987f76ee892STomi Valkeinen 
988f76ee892STomi Valkeinen 	return r;
989f76ee892STomi Valkeinen }
990f76ee892STomi Valkeinen 
dsicm_memory_read(struct omap_dss_device * dssdev,void * buf,size_t size,u16 x,u16 y,u16 w,u16 h)991f76ee892STomi Valkeinen static int dsicm_memory_read(struct omap_dss_device *dssdev,
992f76ee892STomi Valkeinen 		void *buf, size_t size,
993f76ee892STomi Valkeinen 		u16 x, u16 y, u16 w, u16 h)
994f76ee892STomi Valkeinen {
995f76ee892STomi Valkeinen 	struct panel_drv_data *ddata = to_panel_data(dssdev);
996f76ee892STomi Valkeinen 	struct omap_dss_device *in = ddata->in;
997f76ee892STomi Valkeinen 	int r;
998f76ee892STomi Valkeinen 	int first = 1;
999f76ee892STomi Valkeinen 	int plen;
1000f76ee892STomi Valkeinen 	unsigned buf_used = 0;
1001f76ee892STomi Valkeinen 
1002f76ee892STomi Valkeinen 	if (size < w * h * 3)
1003f76ee892STomi Valkeinen 		return -ENOMEM;
1004f76ee892STomi Valkeinen 
1005f76ee892STomi Valkeinen 	mutex_lock(&ddata->lock);
1006f76ee892STomi Valkeinen 
1007f76ee892STomi Valkeinen 	if (!ddata->enabled) {
1008f76ee892STomi Valkeinen 		r = -ENODEV;
1009f76ee892STomi Valkeinen 		goto err1;
1010f76ee892STomi Valkeinen 	}
1011f76ee892STomi Valkeinen 
1012f76ee892STomi Valkeinen 	size = min(w * h * 3,
1013f76ee892STomi Valkeinen 			dssdev->panel.timings.x_res *
1014f76ee892STomi Valkeinen 			dssdev->panel.timings.y_res * 3);
1015f76ee892STomi Valkeinen 
1016f76ee892STomi Valkeinen 	in->ops.dsi->bus_lock(in);
1017f76ee892STomi Valkeinen 
1018f76ee892STomi Valkeinen 	r = dsicm_wake_up(ddata);
1019f76ee892STomi Valkeinen 	if (r)
1020f76ee892STomi Valkeinen 		goto err2;
1021f76ee892STomi Valkeinen 
1022f76ee892STomi Valkeinen 	/* plen 1 or 2 goes into short packet. until checksum error is fixed,
1023f76ee892STomi Valkeinen 	 * use short packets. plen 32 works, but bigger packets seem to cause
1024f76ee892STomi Valkeinen 	 * an error. */
1025f76ee892STomi Valkeinen 	if (size % 2)
1026f76ee892STomi Valkeinen 		plen = 1;
1027f76ee892STomi Valkeinen 	else
1028f76ee892STomi Valkeinen 		plen = 2;
1029f76ee892STomi Valkeinen 
1030f76ee892STomi Valkeinen 	dsicm_set_update_window(ddata, x, y, w, h);
1031f76ee892STomi Valkeinen 
1032f76ee892STomi Valkeinen 	r = in->ops.dsi->set_max_rx_packet_size(in, ddata->channel, plen);
1033f76ee892STomi Valkeinen 	if (r)
1034f76ee892STomi Valkeinen 		goto err2;
1035f76ee892STomi Valkeinen 
1036f76ee892STomi Valkeinen 	while (buf_used < size) {
1037f76ee892STomi Valkeinen 		u8 dcs_cmd = first ? 0x2e : 0x3e;
1038f76ee892STomi Valkeinen 		first = 0;
1039f76ee892STomi Valkeinen 
1040f76ee892STomi Valkeinen 		r = in->ops.dsi->dcs_read(in, ddata->channel, dcs_cmd,
1041f76ee892STomi Valkeinen 				buf + buf_used, size - buf_used);
1042f76ee892STomi Valkeinen 
1043f76ee892STomi Valkeinen 		if (r < 0) {
1044f76ee892STomi Valkeinen 			dev_err(dssdev->dev, "read error\n");
1045f76ee892STomi Valkeinen 			goto err3;
1046f76ee892STomi Valkeinen 		}
1047f76ee892STomi Valkeinen 
1048f76ee892STomi Valkeinen 		buf_used += r;
1049f76ee892STomi Valkeinen 
1050f76ee892STomi Valkeinen 		if (r < plen) {
1051f76ee892STomi Valkeinen 			dev_err(&ddata->pdev->dev, "short read\n");
1052f76ee892STomi Valkeinen 			break;
1053f76ee892STomi Valkeinen 		}
1054f76ee892STomi Valkeinen 
1055f76ee892STomi Valkeinen 		if (signal_pending(current)) {
1056f76ee892STomi Valkeinen 			dev_err(&ddata->pdev->dev, "signal pending, "
1057f76ee892STomi Valkeinen 					"aborting memory read\n");
1058f76ee892STomi Valkeinen 			r = -ERESTARTSYS;
1059f76ee892STomi Valkeinen 			goto err3;
1060f76ee892STomi Valkeinen 		}
1061f76ee892STomi Valkeinen 	}
1062f76ee892STomi Valkeinen 
1063f76ee892STomi Valkeinen 	r = buf_used;
1064f76ee892STomi Valkeinen 
1065f76ee892STomi Valkeinen err3:
1066f76ee892STomi Valkeinen 	in->ops.dsi->set_max_rx_packet_size(in, ddata->channel, 1);
1067f76ee892STomi Valkeinen err2:
1068f76ee892STomi Valkeinen 	in->ops.dsi->bus_unlock(in);
1069f76ee892STomi Valkeinen err1:
1070f76ee892STomi Valkeinen 	mutex_unlock(&ddata->lock);
1071f76ee892STomi Valkeinen 	return r;
1072f76ee892STomi Valkeinen }
1073f76ee892STomi Valkeinen 
dsicm_ulps_work(struct work_struct * work)1074f76ee892STomi Valkeinen static void dsicm_ulps_work(struct work_struct *work)
1075f76ee892STomi Valkeinen {
1076f76ee892STomi Valkeinen 	struct panel_drv_data *ddata = container_of(work, struct panel_drv_data,
1077f76ee892STomi Valkeinen 			ulps_work.work);
1078f76ee892STomi Valkeinen 	struct omap_dss_device *dssdev = &ddata->dssdev;
1079f76ee892STomi Valkeinen 	struct omap_dss_device *in = ddata->in;
1080f76ee892STomi Valkeinen 
1081f76ee892STomi Valkeinen 	mutex_lock(&ddata->lock);
1082f76ee892STomi Valkeinen 
1083f76ee892STomi Valkeinen 	if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || !ddata->enabled) {
1084f76ee892STomi Valkeinen 		mutex_unlock(&ddata->lock);
1085f76ee892STomi Valkeinen 		return;
1086f76ee892STomi Valkeinen 	}
1087f76ee892STomi Valkeinen 
1088f76ee892STomi Valkeinen 	in->ops.dsi->bus_lock(in);
1089f76ee892STomi Valkeinen 
1090f76ee892STomi Valkeinen 	dsicm_enter_ulps(ddata);
1091f76ee892STomi Valkeinen 
1092f76ee892STomi Valkeinen 	in->ops.dsi->bus_unlock(in);
1093f76ee892STomi Valkeinen 	mutex_unlock(&ddata->lock);
1094f76ee892STomi Valkeinen }
1095f76ee892STomi Valkeinen 
1096f76ee892STomi Valkeinen static struct omap_dss_driver dsicm_ops = {
1097f76ee892STomi Valkeinen 	.connect	= dsicm_connect,
1098f76ee892STomi Valkeinen 	.disconnect	= dsicm_disconnect,
1099f76ee892STomi Valkeinen 
1100f76ee892STomi Valkeinen 	.enable		= dsicm_enable,
1101f76ee892STomi Valkeinen 	.disable	= dsicm_disable,
1102f76ee892STomi Valkeinen 
1103f76ee892STomi Valkeinen 	.update		= dsicm_update,
1104f76ee892STomi Valkeinen 	.sync		= dsicm_sync,
1105f76ee892STomi Valkeinen 
1106f76ee892STomi Valkeinen 	.get_resolution	= dsicm_get_resolution,
1107f76ee892STomi Valkeinen 	.get_recommended_bpp = omapdss_default_get_recommended_bpp,
1108f76ee892STomi Valkeinen 
1109f76ee892STomi Valkeinen 	.enable_te	= dsicm_enable_te,
1110f76ee892STomi Valkeinen 	.get_te		= dsicm_get_te,
1111f76ee892STomi Valkeinen 
1112f76ee892STomi Valkeinen 	.memory_read	= dsicm_memory_read,
1113f76ee892STomi Valkeinen };
1114f76ee892STomi Valkeinen 
dsicm_probe(struct platform_device * pdev)1115f76ee892STomi Valkeinen static int dsicm_probe(struct platform_device *pdev)
1116f76ee892STomi Valkeinen {
1117f76ee892STomi Valkeinen 	struct backlight_properties props;
1118f76ee892STomi Valkeinen 	struct panel_drv_data *ddata;
1119f76ee892STomi Valkeinen 	struct backlight_device *bldev = NULL;
1120f76ee892STomi Valkeinen 	struct device *dev = &pdev->dev;
1121f76ee892STomi Valkeinen 	struct omap_dss_device *dssdev;
1122f76ee892STomi Valkeinen 	int r;
1123f76ee892STomi Valkeinen 
1124f76ee892STomi Valkeinen 	dev_dbg(dev, "probe\n");
1125f76ee892STomi Valkeinen 
11260984097cSPeter Ujfalusi 	if (!pdev->dev.of_node)
11270984097cSPeter Ujfalusi 		return -ENODEV;
11280984097cSPeter Ujfalusi 
1129f76ee892STomi Valkeinen 	ddata = devm_kzalloc(dev, sizeof(*ddata), GFP_KERNEL);
1130f76ee892STomi Valkeinen 	if (!ddata)
1131f76ee892STomi Valkeinen 		return -ENOMEM;
1132f76ee892STomi Valkeinen 
1133f76ee892STomi Valkeinen 	platform_set_drvdata(pdev, ddata);
1134f76ee892STomi Valkeinen 	ddata->pdev = pdev;
1135f76ee892STomi Valkeinen 
113667c366deSDmitry Torokhov 	ddata->in = omapdss_of_find_source_for_first_ep(pdev->dev.of_node);
113767c366deSDmitry Torokhov 	r = PTR_ERR_OR_ZERO(ddata->in);
113867c366deSDmitry Torokhov 	if (r) {
113967c366deSDmitry Torokhov 		dev_err(&pdev->dev, "failed to find video source: %d\n", r);
1140f76ee892STomi Valkeinen 		return r;
114167c366deSDmitry Torokhov 	}
1142f76ee892STomi Valkeinen 
1143f76ee892STomi Valkeinen 	ddata->timings.x_res = 864;
1144f76ee892STomi Valkeinen 	ddata->timings.y_res = 480;
1145f76ee892STomi Valkeinen 	ddata->timings.pixelclock = 864 * 480 * 60;
1146f76ee892STomi Valkeinen 
1147f76ee892STomi Valkeinen 	dssdev = &ddata->dssdev;
1148f76ee892STomi Valkeinen 	dssdev->dev = dev;
1149f76ee892STomi Valkeinen 	dssdev->driver = &dsicm_ops;
1150f76ee892STomi Valkeinen 	dssdev->panel.timings = ddata->timings;
1151f76ee892STomi Valkeinen 	dssdev->type = OMAP_DISPLAY_TYPE_DSI;
1152f76ee892STomi Valkeinen 	dssdev->owner = THIS_MODULE;
1153f76ee892STomi Valkeinen 
1154f76ee892STomi Valkeinen 	dssdev->panel.dsi_pix_fmt = OMAP_DSS_DSI_FMT_RGB888;
1155f76ee892STomi Valkeinen 	dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE |
1156f76ee892STomi Valkeinen 		OMAP_DSS_DISPLAY_CAP_TEAR_ELIM;
1157f76ee892STomi Valkeinen 
1158f76ee892STomi Valkeinen 	r = omapdss_register_display(dssdev);
1159f76ee892STomi Valkeinen 	if (r) {
1160f76ee892STomi Valkeinen 		dev_err(dev, "Failed to register panel\n");
1161f76ee892STomi Valkeinen 		goto err_reg;
1162f76ee892STomi Valkeinen 	}
1163f76ee892STomi Valkeinen 
1164f76ee892STomi Valkeinen 	mutex_init(&ddata->lock);
1165f76ee892STomi Valkeinen 
1166f76ee892STomi Valkeinen 	atomic_set(&ddata->do_update, 0);
1167f76ee892STomi Valkeinen 
116867c366deSDmitry Torokhov 	ddata->reset_gpio = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_LOW);
116967c366deSDmitry Torokhov 	r = PTR_ERR_OR_ZERO(ddata->reset_gpio);
1170f76ee892STomi Valkeinen 	if (r) {
117167c366deSDmitry Torokhov 		dev_err(&pdev->dev, "Failed to request reset gpio: %d\n", r);
1172f76ee892STomi Valkeinen 		return r;
1173f76ee892STomi Valkeinen 	}
1174f76ee892STomi Valkeinen 
117567c366deSDmitry Torokhov 	gpiod_set_consumer_name(ddata->reset_gpio, "taal rst");
117667c366deSDmitry Torokhov 
117767c366deSDmitry Torokhov 	ddata->ext_te_gpio = devm_gpiod_get_optional(&pdev->dev, "te",
117867c366deSDmitry Torokhov 						     GPIOD_IN);
117967c366deSDmitry Torokhov 	r = PTR_ERR_OR_ZERO(ddata->ext_te_gpio);
118067c366deSDmitry Torokhov 	if (r) {
118167c366deSDmitry Torokhov 		dev_err(&pdev->dev, "Failed to request TE gpio: %d\n", r);
118267c366deSDmitry Torokhov 		return r;
118367c366deSDmitry Torokhov 	}
118467c366deSDmitry Torokhov 
118567c366deSDmitry Torokhov 	if (ddata->ext_te_gpio) {
118667c366deSDmitry Torokhov 		gpiod_set_consumer_name(ddata->ext_te_gpio, "taal irq");
118767c366deSDmitry Torokhov 
118867c366deSDmitry Torokhov 		r = devm_request_irq(dev, gpiod_to_irq(ddata->ext_te_gpio),
1189f76ee892STomi Valkeinen 				dsicm_te_isr,
1190f76ee892STomi Valkeinen 				IRQF_TRIGGER_RISING,
1191f76ee892STomi Valkeinen 				"taal vsync", ddata);
1192f76ee892STomi Valkeinen 
1193f76ee892STomi Valkeinen 		if (r) {
1194f76ee892STomi Valkeinen 			dev_err(dev, "IRQ request failed\n");
1195f76ee892STomi Valkeinen 			return r;
1196f76ee892STomi Valkeinen 		}
1197f76ee892STomi Valkeinen 
1198f76ee892STomi Valkeinen 		INIT_DEFERRABLE_WORK(&ddata->te_timeout_work,
1199f76ee892STomi Valkeinen 					dsicm_te_timeout_work_callback);
1200f76ee892STomi Valkeinen 
1201f76ee892STomi Valkeinen 		dev_dbg(dev, "Using GPIO TE\n");
1202f76ee892STomi Valkeinen 	}
1203f76ee892STomi Valkeinen 
1204f76ee892STomi Valkeinen 	INIT_DELAYED_WORK(&ddata->ulps_work, dsicm_ulps_work);
1205f76ee892STomi Valkeinen 
1206f76ee892STomi Valkeinen 	dsicm_hw_reset(ddata);
1207f76ee892STomi Valkeinen 
1208f76ee892STomi Valkeinen 	if (ddata->use_dsi_backlight) {
1209f76ee892STomi Valkeinen 		memset(&props, 0, sizeof(struct backlight_properties));
1210f76ee892STomi Valkeinen 		props.max_brightness = 255;
1211f76ee892STomi Valkeinen 
1212f76ee892STomi Valkeinen 		props.type = BACKLIGHT_RAW;
1213f76ee892STomi Valkeinen 		bldev = backlight_device_register(dev_name(dev),
1214f76ee892STomi Valkeinen 				dev, ddata, &dsicm_bl_ops, &props);
1215f76ee892STomi Valkeinen 		if (IS_ERR(bldev)) {
1216f76ee892STomi Valkeinen 			r = PTR_ERR(bldev);
1217b59ac9a3SBhaktipriya Shridhar 			goto err_reg;
1218f76ee892STomi Valkeinen 		}
1219f76ee892STomi Valkeinen 
1220f76ee892STomi Valkeinen 		ddata->bldev = bldev;
1221f76ee892STomi Valkeinen 
1222f76ee892STomi Valkeinen 		bldev->props.fb_blank = FB_BLANK_UNBLANK;
1223f76ee892STomi Valkeinen 		bldev->props.power = FB_BLANK_UNBLANK;
1224f76ee892STomi Valkeinen 		bldev->props.brightness = 255;
1225f76ee892STomi Valkeinen 
1226f76ee892STomi Valkeinen 		dsicm_bl_update_status(bldev);
1227f76ee892STomi Valkeinen 	}
1228f76ee892STomi Valkeinen 
1229f76ee892STomi Valkeinen 	r = sysfs_create_group(&dev->kobj, &dsicm_attr_group);
1230f76ee892STomi Valkeinen 	if (r) {
1231f76ee892STomi Valkeinen 		dev_err(dev, "failed to create sysfs files\n");
1232f76ee892STomi Valkeinen 		goto err_sysfs_create;
1233f76ee892STomi Valkeinen 	}
1234f76ee892STomi Valkeinen 
1235f76ee892STomi Valkeinen 	return 0;
1236f76ee892STomi Valkeinen 
1237f76ee892STomi Valkeinen err_sysfs_create:
1238f76ee892STomi Valkeinen 	if (bldev != NULL)
1239f76ee892STomi Valkeinen 		backlight_device_unregister(bldev);
1240f76ee892STomi Valkeinen err_reg:
1241f76ee892STomi Valkeinen 	return r;
1242f76ee892STomi Valkeinen }
1243f76ee892STomi Valkeinen 
dsicm_remove(struct platform_device * pdev)1244f76ee892STomi Valkeinen static int __exit dsicm_remove(struct platform_device *pdev)
1245f76ee892STomi Valkeinen {
1246f76ee892STomi Valkeinen 	struct panel_drv_data *ddata = platform_get_drvdata(pdev);
1247f76ee892STomi Valkeinen 	struct omap_dss_device *dssdev = &ddata->dssdev;
1248f76ee892STomi Valkeinen 	struct backlight_device *bldev;
1249f76ee892STomi Valkeinen 
1250f76ee892STomi Valkeinen 	dev_dbg(&pdev->dev, "remove\n");
1251f76ee892STomi Valkeinen 
1252f76ee892STomi Valkeinen 	omapdss_unregister_display(dssdev);
1253f76ee892STomi Valkeinen 
1254f76ee892STomi Valkeinen 	dsicm_disable(dssdev);
1255f76ee892STomi Valkeinen 	dsicm_disconnect(dssdev);
1256f76ee892STomi Valkeinen 
1257f76ee892STomi Valkeinen 	sysfs_remove_group(&pdev->dev.kobj, &dsicm_attr_group);
1258f76ee892STomi Valkeinen 
1259f76ee892STomi Valkeinen 	bldev = ddata->bldev;
1260f76ee892STomi Valkeinen 	if (bldev != NULL) {
1261f76ee892STomi Valkeinen 		bldev->props.power = FB_BLANK_POWERDOWN;
1262f76ee892STomi Valkeinen 		dsicm_bl_update_status(bldev);
1263f76ee892STomi Valkeinen 		backlight_device_unregister(bldev);
1264f76ee892STomi Valkeinen 	}
1265f76ee892STomi Valkeinen 
1266f76ee892STomi Valkeinen 	omap_dss_put_device(ddata->in);
1267f76ee892STomi Valkeinen 
1268f76ee892STomi Valkeinen 	dsicm_cancel_ulps_work(ddata);
1269f76ee892STomi Valkeinen 
1270f76ee892STomi Valkeinen 	/* reset, to be sure that the panel is in a valid state */
1271f76ee892STomi Valkeinen 	dsicm_hw_reset(ddata);
1272f76ee892STomi Valkeinen 
1273f76ee892STomi Valkeinen 	return 0;
1274f76ee892STomi Valkeinen }
1275f76ee892STomi Valkeinen 
1276f76ee892STomi Valkeinen static const struct of_device_id dsicm_of_match[] = {
1277f76ee892STomi Valkeinen 	{ .compatible = "omapdss,panel-dsi-cm", },
1278f76ee892STomi Valkeinen 	{},
1279f76ee892STomi Valkeinen };
1280f76ee892STomi Valkeinen 
1281f76ee892STomi Valkeinen MODULE_DEVICE_TABLE(of, dsicm_of_match);
1282f76ee892STomi Valkeinen 
1283f76ee892STomi Valkeinen static struct platform_driver dsicm_driver = {
1284f76ee892STomi Valkeinen 	.probe = dsicm_probe,
1285f76ee892STomi Valkeinen 	.remove = __exit_p(dsicm_remove),
1286f76ee892STomi Valkeinen 	.driver = {
1287f76ee892STomi Valkeinen 		.name = "panel-dsi-cm",
1288f76ee892STomi Valkeinen 		.of_match_table = dsicm_of_match,
1289f76ee892STomi Valkeinen 		.suppress_bind_attrs = true,
1290f76ee892STomi Valkeinen 	},
1291f76ee892STomi Valkeinen };
1292f76ee892STomi Valkeinen 
1293f76ee892STomi Valkeinen module_platform_driver(dsicm_driver);
1294f76ee892STomi Valkeinen 
1295f76ee892STomi Valkeinen MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@ti.com>");
1296f76ee892STomi Valkeinen MODULE_DESCRIPTION("Generic DSI Command Mode Panel Driver");
1297f76ee892STomi Valkeinen MODULE_LICENSE("GPL");
1298