1/*
2 * linux/drivers/video/omap2/dss/display.c
3 *
4 * Copyright (C) 2009 Nokia Corporation
5 * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com>
6 *
7 * Some code and ideas taken from drivers/video/omap/ driver
8 * by Imre Deak.
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License version 2 as published by
12 * the Free Software Foundation.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
17 * more details.
18 *
19 * You should have received a copy of the GNU General Public License along with
20 * this program.  If not, see <http://www.gnu.org/licenses/>.
21 */
22
23#define DSS_SUBSYS_NAME "DISPLAY"
24
25#include <linux/kernel.h>
26#include <linux/module.h>
27#include <linux/jiffies.h>
28#include <linux/platform_device.h>
29
30#include <video/omapdss.h>
31#include "dss.h"
32#include "dss_features.h"
33
34static ssize_t display_enabled_show(struct device *dev,
35		struct device_attribute *attr, char *buf)
36{
37	struct omap_dss_device *dssdev = to_dss_device(dev);
38	bool enabled = dssdev->state != OMAP_DSS_DISPLAY_DISABLED;
39
40	return snprintf(buf, PAGE_SIZE, "%d\n", enabled);
41}
42
43static ssize_t display_enabled_store(struct device *dev,
44		struct device_attribute *attr,
45		const char *buf, size_t size)
46{
47	struct omap_dss_device *dssdev = to_dss_device(dev);
48	int r;
49	bool enabled;
50
51	r = strtobool(buf, &enabled);
52	if (r)
53		return r;
54
55	if (enabled != (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)) {
56		if (enabled) {
57			r = dssdev->driver->enable(dssdev);
58			if (r)
59				return r;
60		} else {
61			dssdev->driver->disable(dssdev);
62		}
63	}
64
65	return size;
66}
67
68static ssize_t display_tear_show(struct device *dev,
69		struct device_attribute *attr, char *buf)
70{
71	struct omap_dss_device *dssdev = to_dss_device(dev);
72	return snprintf(buf, PAGE_SIZE, "%d\n",
73			dssdev->driver->get_te ?
74			dssdev->driver->get_te(dssdev) : 0);
75}
76
77static ssize_t display_tear_store(struct device *dev,
78		struct device_attribute *attr, const char *buf, size_t size)
79{
80	struct omap_dss_device *dssdev = to_dss_device(dev);
81	int r;
82	bool te;
83
84	if (!dssdev->driver->enable_te || !dssdev->driver->get_te)
85		return -ENOENT;
86
87	r = strtobool(buf, &te);
88	if (r)
89		return r;
90
91	r = dssdev->driver->enable_te(dssdev, te);
92	if (r)
93		return r;
94
95	return size;
96}
97
98static ssize_t display_timings_show(struct device *dev,
99		struct device_attribute *attr, char *buf)
100{
101	struct omap_dss_device *dssdev = to_dss_device(dev);
102	struct omap_video_timings t;
103
104	if (!dssdev->driver->get_timings)
105		return -ENOENT;
106
107	dssdev->driver->get_timings(dssdev, &t);
108
109	return snprintf(buf, PAGE_SIZE, "%u,%u/%u/%u/%u,%u/%u/%u/%u\n",
110			t.pixel_clock,
111			t.x_res, t.hfp, t.hbp, t.hsw,
112			t.y_res, t.vfp, t.vbp, t.vsw);
113}
114
115static ssize_t display_timings_store(struct device *dev,
116		struct device_attribute *attr, const char *buf, size_t size)
117{
118	struct omap_dss_device *dssdev = to_dss_device(dev);
119	struct omap_video_timings t;
120	int r, found;
121
122	if (!dssdev->driver->set_timings || !dssdev->driver->check_timings)
123		return -ENOENT;
124
125	found = 0;
126#ifdef CONFIG_OMAP2_DSS_VENC
127	if (strncmp("pal", buf, 3) == 0) {
128		t = omap_dss_pal_timings;
129		found = 1;
130	} else if (strncmp("ntsc", buf, 4) == 0) {
131		t = omap_dss_ntsc_timings;
132		found = 1;
133	}
134#endif
135	if (!found && sscanf(buf, "%u,%hu/%hu/%hu/%hu,%hu/%hu/%hu/%hu",
136				&t.pixel_clock,
137				&t.x_res, &t.hfp, &t.hbp, &t.hsw,
138				&t.y_res, &t.vfp, &t.vbp, &t.vsw) != 9)
139		return -EINVAL;
140
141	r = dssdev->driver->check_timings(dssdev, &t);
142	if (r)
143		return r;
144
145	dssdev->driver->set_timings(dssdev, &t);
146
147	return size;
148}
149
150static ssize_t display_rotate_show(struct device *dev,
151		struct device_attribute *attr, char *buf)
152{
153	struct omap_dss_device *dssdev = to_dss_device(dev);
154	int rotate;
155	if (!dssdev->driver->get_rotate)
156		return -ENOENT;
157	rotate = dssdev->driver->get_rotate(dssdev);
158	return snprintf(buf, PAGE_SIZE, "%u\n", rotate);
159}
160
161static ssize_t display_rotate_store(struct device *dev,
162		struct device_attribute *attr, const char *buf, size_t size)
163{
164	struct omap_dss_device *dssdev = to_dss_device(dev);
165	int rot, r;
166
167	if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate)
168		return -ENOENT;
169
170	r = kstrtoint(buf, 0, &rot);
171	if (r)
172		return r;
173
174	r = dssdev->driver->set_rotate(dssdev, rot);
175	if (r)
176		return r;
177
178	return size;
179}
180
181static ssize_t display_mirror_show(struct device *dev,
182		struct device_attribute *attr, char *buf)
183{
184	struct omap_dss_device *dssdev = to_dss_device(dev);
185	int mirror;
186	if (!dssdev->driver->get_mirror)
187		return -ENOENT;
188	mirror = dssdev->driver->get_mirror(dssdev);
189	return snprintf(buf, PAGE_SIZE, "%u\n", mirror);
190}
191
192static ssize_t display_mirror_store(struct device *dev,
193		struct device_attribute *attr, const char *buf, size_t size)
194{
195	struct omap_dss_device *dssdev = to_dss_device(dev);
196	int r;
197	bool mirror;
198
199	if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror)
200		return -ENOENT;
201
202	r = strtobool(buf, &mirror);
203	if (r)
204		return r;
205
206	r = dssdev->driver->set_mirror(dssdev, mirror);
207	if (r)
208		return r;
209
210	return size;
211}
212
213static ssize_t display_wss_show(struct device *dev,
214		struct device_attribute *attr, char *buf)
215{
216	struct omap_dss_device *dssdev = to_dss_device(dev);
217	unsigned int wss;
218
219	if (!dssdev->driver->get_wss)
220		return -ENOENT;
221
222	wss = dssdev->driver->get_wss(dssdev);
223
224	return snprintf(buf, PAGE_SIZE, "0x%05x\n", wss);
225}
226
227static ssize_t display_wss_store(struct device *dev,
228		struct device_attribute *attr, const char *buf, size_t size)
229{
230	struct omap_dss_device *dssdev = to_dss_device(dev);
231	u32 wss;
232	int r;
233
234	if (!dssdev->driver->get_wss || !dssdev->driver->set_wss)
235		return -ENOENT;
236
237	r = kstrtou32(buf, 0, &wss);
238	if (r)
239		return r;
240
241	if (wss > 0xfffff)
242		return -EINVAL;
243
244	r = dssdev->driver->set_wss(dssdev, wss);
245	if (r)
246		return r;
247
248	return size;
249}
250
251static DEVICE_ATTR(enabled, S_IRUGO|S_IWUSR,
252		display_enabled_show, display_enabled_store);
253static DEVICE_ATTR(tear_elim, S_IRUGO|S_IWUSR,
254		display_tear_show, display_tear_store);
255static DEVICE_ATTR(timings, S_IRUGO|S_IWUSR,
256		display_timings_show, display_timings_store);
257static DEVICE_ATTR(rotate, S_IRUGO|S_IWUSR,
258		display_rotate_show, display_rotate_store);
259static DEVICE_ATTR(mirror, S_IRUGO|S_IWUSR,
260		display_mirror_show, display_mirror_store);
261static DEVICE_ATTR(wss, S_IRUGO|S_IWUSR,
262		display_wss_show, display_wss_store);
263
264static struct device_attribute *display_sysfs_attrs[] = {
265	&dev_attr_enabled,
266	&dev_attr_tear_elim,
267	&dev_attr_timings,
268	&dev_attr_rotate,
269	&dev_attr_mirror,
270	&dev_attr_wss,
271	NULL
272};
273
274void omapdss_default_get_resolution(struct omap_dss_device *dssdev,
275			u16 *xres, u16 *yres)
276{
277	*xres = dssdev->panel.timings.x_res;
278	*yres = dssdev->panel.timings.y_res;
279}
280EXPORT_SYMBOL(omapdss_default_get_resolution);
281
282int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev)
283{
284	switch (dssdev->type) {
285	case OMAP_DISPLAY_TYPE_DPI:
286		if (dssdev->phy.dpi.data_lines == 24)
287			return 24;
288		else
289			return 16;
290
291	case OMAP_DISPLAY_TYPE_DBI:
292		if (dssdev->ctrl.pixel_size == 24)
293			return 24;
294		else
295			return 16;
296	case OMAP_DISPLAY_TYPE_DSI:
297		if (dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt) > 16)
298			return 24;
299		else
300			return 16;
301	case OMAP_DISPLAY_TYPE_VENC:
302	case OMAP_DISPLAY_TYPE_SDI:
303	case OMAP_DISPLAY_TYPE_HDMI:
304		return 24;
305	default:
306		BUG();
307	}
308}
309EXPORT_SYMBOL(omapdss_default_get_recommended_bpp);
310
311/* Checks if replication logic should be used. Only use for active matrix,
312 * when overlay is in RGB12U or RGB16 mode, and LCD interface is
313 * 18bpp or 24bpp */
314bool dss_use_replication(struct omap_dss_device *dssdev,
315		enum omap_color_mode mode)
316{
317	int bpp;
318
319	if (mode != OMAP_DSS_COLOR_RGB12U && mode != OMAP_DSS_COLOR_RGB16)
320		return false;
321
322	if (dssdev->type == OMAP_DISPLAY_TYPE_DPI &&
323			(dssdev->panel.config & OMAP_DSS_LCD_TFT) == 0)
324		return false;
325
326	switch (dssdev->type) {
327	case OMAP_DISPLAY_TYPE_DPI:
328		bpp = dssdev->phy.dpi.data_lines;
329		break;
330	case OMAP_DISPLAY_TYPE_HDMI:
331	case OMAP_DISPLAY_TYPE_VENC:
332	case OMAP_DISPLAY_TYPE_SDI:
333		bpp = 24;
334		break;
335	case OMAP_DISPLAY_TYPE_DBI:
336		bpp = dssdev->ctrl.pixel_size;
337		break;
338	case OMAP_DISPLAY_TYPE_DSI:
339		bpp = dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt);
340		break;
341	default:
342		BUG();
343	}
344
345	return bpp > 16;
346}
347
348void dss_init_device(struct platform_device *pdev,
349		struct omap_dss_device *dssdev)
350{
351	struct device_attribute *attr;
352	int i;
353	int r;
354
355	switch (dssdev->type) {
356#ifdef CONFIG_OMAP2_DSS_DPI
357	case OMAP_DISPLAY_TYPE_DPI:
358		r = dpi_init_display(dssdev);
359		break;
360#endif
361#ifdef CONFIG_OMAP2_DSS_RFBI
362	case OMAP_DISPLAY_TYPE_DBI:
363		r = rfbi_init_display(dssdev);
364		break;
365#endif
366#ifdef CONFIG_OMAP2_DSS_VENC
367	case OMAP_DISPLAY_TYPE_VENC:
368		r = venc_init_display(dssdev);
369		break;
370#endif
371#ifdef CONFIG_OMAP2_DSS_SDI
372	case OMAP_DISPLAY_TYPE_SDI:
373		r = sdi_init_display(dssdev);
374		break;
375#endif
376#ifdef CONFIG_OMAP2_DSS_DSI
377	case OMAP_DISPLAY_TYPE_DSI:
378		r = dsi_init_display(dssdev);
379		break;
380#endif
381	case OMAP_DISPLAY_TYPE_HDMI:
382		r = hdmi_init_display(dssdev);
383		break;
384	default:
385		DSSERR("Support for display '%s' not compiled in.\n",
386				dssdev->name);
387		return;
388	}
389
390	if (r) {
391		DSSERR("failed to init display %s\n", dssdev->name);
392		return;
393	}
394
395	/* create device sysfs files */
396	i = 0;
397	while ((attr = display_sysfs_attrs[i++]) != NULL) {
398		r = device_create_file(&dssdev->dev, attr);
399		if (r)
400			DSSERR("failed to create sysfs file\n");
401	}
402
403	/* create display? sysfs links */
404	r = sysfs_create_link(&pdev->dev.kobj, &dssdev->dev.kobj,
405			dev_name(&dssdev->dev));
406	if (r)
407		DSSERR("failed to create sysfs display link\n");
408}
409
410void dss_uninit_device(struct platform_device *pdev,
411		struct omap_dss_device *dssdev)
412{
413	struct device_attribute *attr;
414	int i = 0;
415
416	sysfs_remove_link(&pdev->dev.kobj, dev_name(&dssdev->dev));
417
418	while ((attr = display_sysfs_attrs[i++]) != NULL)
419		device_remove_file(&dssdev->dev, attr);
420
421	if (dssdev->manager)
422		dssdev->manager->unset_device(dssdev->manager);
423}
424
425static int dss_suspend_device(struct device *dev, void *data)
426{
427	int r;
428	struct omap_dss_device *dssdev = to_dss_device(dev);
429
430	if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
431		dssdev->activate_after_resume = false;
432		return 0;
433	}
434
435	if (!dssdev->driver->suspend) {
436		DSSERR("display '%s' doesn't implement suspend\n",
437				dssdev->name);
438		return -ENOSYS;
439	}
440
441	r = dssdev->driver->suspend(dssdev);
442	if (r)
443		return r;
444
445	dssdev->activate_after_resume = true;
446
447	return 0;
448}
449
450int dss_suspend_all_devices(void)
451{
452	int r;
453	struct bus_type *bus = dss_get_bus();
454
455	r = bus_for_each_dev(bus, NULL, NULL, dss_suspend_device);
456	if (r) {
457		/* resume all displays that were suspended */
458		dss_resume_all_devices();
459		return r;
460	}
461
462	return 0;
463}
464
465static int dss_resume_device(struct device *dev, void *data)
466{
467	int r;
468	struct omap_dss_device *dssdev = to_dss_device(dev);
469
470	if (dssdev->activate_after_resume && dssdev->driver->resume) {
471		r = dssdev->driver->resume(dssdev);
472		if (r)
473			return r;
474	}
475
476	dssdev->activate_after_resume = false;
477
478	return 0;
479}
480
481int dss_resume_all_devices(void)
482{
483	struct bus_type *bus = dss_get_bus();
484
485	return bus_for_each_dev(bus, NULL, NULL, dss_resume_device);
486}
487
488static int dss_disable_device(struct device *dev, void *data)
489{
490	struct omap_dss_device *dssdev = to_dss_device(dev);
491
492	if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)
493		dssdev->driver->disable(dssdev);
494
495	return 0;
496}
497
498void dss_disable_all_devices(void)
499{
500	struct bus_type *bus = dss_get_bus();
501	bus_for_each_dev(bus, NULL, NULL, dss_disable_device);
502}
503
504
505void omap_dss_get_device(struct omap_dss_device *dssdev)
506{
507	get_device(&dssdev->dev);
508}
509EXPORT_SYMBOL(omap_dss_get_device);
510
511void omap_dss_put_device(struct omap_dss_device *dssdev)
512{
513	put_device(&dssdev->dev);
514}
515EXPORT_SYMBOL(omap_dss_put_device);
516
517/* ref count of the found device is incremented. ref count
518 * of from-device is decremented. */
519struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from)
520{
521	struct device *dev;
522	struct device *dev_start = NULL;
523	struct omap_dss_device *dssdev = NULL;
524
525	int match(struct device *dev, void *data)
526	{
527		return 1;
528	}
529
530	if (from)
531		dev_start = &from->dev;
532	dev = bus_find_device(dss_get_bus(), dev_start, NULL, match);
533	if (dev)
534		dssdev = to_dss_device(dev);
535	if (from)
536		put_device(&from->dev);
537
538	return dssdev;
539}
540EXPORT_SYMBOL(omap_dss_get_next_device);
541
542struct omap_dss_device *omap_dss_find_device(void *data,
543		int (*match)(struct omap_dss_device *dssdev, void *data))
544{
545	struct omap_dss_device *dssdev = NULL;
546
547	while ((dssdev = omap_dss_get_next_device(dssdev)) != NULL) {
548		if (match(dssdev, data))
549			return dssdev;
550	}
551
552	return NULL;
553}
554EXPORT_SYMBOL(omap_dss_find_device);
555
556int omap_dss_start_device(struct omap_dss_device *dssdev)
557{
558	if (!dssdev->driver) {
559		DSSDBG("no driver\n");
560		return -ENODEV;
561	}
562
563	if (!try_module_get(dssdev->dev.driver->owner)) {
564		return -ENODEV;
565	}
566
567	return 0;
568}
569EXPORT_SYMBOL(omap_dss_start_device);
570
571void omap_dss_stop_device(struct omap_dss_device *dssdev)
572{
573	module_put(dssdev->dev.driver->owner);
574}
575EXPORT_SYMBOL(omap_dss_stop_device);
576
577