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