Commit | Line | Data |
---|---|---|
f76ee892 TV |
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 | #include <linux/of.h> | |
30 | ||
62d9e44e | 31 | #include <video/omapfb_dss.h> |
f76ee892 TV |
32 | #include "dss.h" |
33 | #include "dss_features.h" | |
34 | ||
35 | void omapdss_default_get_resolution(struct omap_dss_device *dssdev, | |
36 | u16 *xres, u16 *yres) | |
37 | { | |
38 | *xres = dssdev->panel.timings.x_res; | |
39 | *yres = dssdev->panel.timings.y_res; | |
40 | } | |
41 | EXPORT_SYMBOL(omapdss_default_get_resolution); | |
42 | ||
43 | int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev) | |
44 | { | |
45 | switch (dssdev->type) { | |
46 | case OMAP_DISPLAY_TYPE_DPI: | |
47 | if (dssdev->phy.dpi.data_lines == 24) | |
48 | return 24; | |
49 | else | |
50 | return 16; | |
51 | ||
52 | case OMAP_DISPLAY_TYPE_DBI: | |
53 | if (dssdev->ctrl.pixel_size == 24) | |
54 | return 24; | |
55 | else | |
56 | return 16; | |
57 | case OMAP_DISPLAY_TYPE_DSI: | |
58 | if (dsi_get_pixel_size(dssdev->panel.dsi_pix_fmt) > 16) | |
59 | return 24; | |
60 | else | |
61 | return 16; | |
62 | case OMAP_DISPLAY_TYPE_VENC: | |
63 | case OMAP_DISPLAY_TYPE_SDI: | |
64 | case OMAP_DISPLAY_TYPE_HDMI: | |
65 | case OMAP_DISPLAY_TYPE_DVI: | |
66 | return 24; | |
67 | default: | |
68 | BUG(); | |
69 | return 0; | |
70 | } | |
71 | } | |
72 | EXPORT_SYMBOL(omapdss_default_get_recommended_bpp); | |
73 | ||
74 | void omapdss_default_get_timings(struct omap_dss_device *dssdev, | |
75 | struct omap_video_timings *timings) | |
76 | { | |
77 | *timings = dssdev->panel.timings; | |
78 | } | |
79 | EXPORT_SYMBOL(omapdss_default_get_timings); | |
80 | ||
81 | int dss_suspend_all_devices(void) | |
82 | { | |
83 | struct omap_dss_device *dssdev = NULL; | |
84 | ||
85 | for_each_dss_dev(dssdev) { | |
86 | if (!dssdev->driver) | |
87 | continue; | |
88 | ||
89 | if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { | |
90 | dssdev->driver->disable(dssdev); | |
91 | dssdev->activate_after_resume = true; | |
92 | } else { | |
93 | dssdev->activate_after_resume = false; | |
94 | } | |
95 | } | |
96 | ||
97 | return 0; | |
98 | } | |
99 | ||
100 | int dss_resume_all_devices(void) | |
101 | { | |
102 | struct omap_dss_device *dssdev = NULL; | |
103 | ||
104 | for_each_dss_dev(dssdev) { | |
105 | if (!dssdev->driver) | |
106 | continue; | |
107 | ||
108 | if (dssdev->activate_after_resume) { | |
109 | dssdev->driver->enable(dssdev); | |
110 | dssdev->activate_after_resume = false; | |
111 | } | |
112 | } | |
113 | ||
114 | return 0; | |
115 | } | |
116 | ||
117 | void dss_disable_all_devices(void) | |
118 | { | |
119 | struct omap_dss_device *dssdev = NULL; | |
120 | ||
121 | for_each_dss_dev(dssdev) { | |
122 | if (!dssdev->driver) | |
123 | continue; | |
124 | ||
125 | if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) | |
126 | dssdev->driver->disable(dssdev); | |
127 | } | |
128 | } | |
129 | ||
130 | static LIST_HEAD(panel_list); | |
131 | static DEFINE_MUTEX(panel_list_mutex); | |
132 | static int disp_num_counter; | |
133 | ||
134 | int omapdss_register_display(struct omap_dss_device *dssdev) | |
135 | { | |
136 | struct omap_dss_driver *drv = dssdev->driver; | |
137 | int id; | |
138 | ||
139 | /* | |
140 | * Note: this presumes all the displays are either using DT or non-DT, | |
141 | * which normally should be the case. This also presumes that all | |
142 | * displays either have an DT alias, or none has. | |
143 | */ | |
144 | ||
145 | if (dssdev->dev->of_node) { | |
146 | id = of_alias_get_id(dssdev->dev->of_node, "display"); | |
147 | ||
148 | if (id < 0) | |
149 | id = disp_num_counter++; | |
150 | } else { | |
151 | id = disp_num_counter++; | |
152 | } | |
153 | ||
154 | snprintf(dssdev->alias, sizeof(dssdev->alias), "display%d", id); | |
155 | ||
156 | /* Use 'label' property for name, if it exists */ | |
157 | if (dssdev->dev->of_node) | |
158 | of_property_read_string(dssdev->dev->of_node, "label", | |
159 | &dssdev->name); | |
160 | ||
161 | if (dssdev->name == NULL) | |
162 | dssdev->name = dssdev->alias; | |
163 | ||
164 | if (drv && drv->get_resolution == NULL) | |
165 | drv->get_resolution = omapdss_default_get_resolution; | |
166 | if (drv && drv->get_recommended_bpp == NULL) | |
167 | drv->get_recommended_bpp = omapdss_default_get_recommended_bpp; | |
168 | if (drv && drv->get_timings == NULL) | |
169 | drv->get_timings = omapdss_default_get_timings; | |
170 | ||
171 | mutex_lock(&panel_list_mutex); | |
172 | list_add_tail(&dssdev->panel_list, &panel_list); | |
173 | mutex_unlock(&panel_list_mutex); | |
174 | return 0; | |
175 | } | |
176 | EXPORT_SYMBOL(omapdss_register_display); | |
177 | ||
178 | void omapdss_unregister_display(struct omap_dss_device *dssdev) | |
179 | { | |
180 | mutex_lock(&panel_list_mutex); | |
181 | list_del(&dssdev->panel_list); | |
182 | mutex_unlock(&panel_list_mutex); | |
183 | } | |
184 | EXPORT_SYMBOL(omapdss_unregister_display); | |
185 | ||
186 | struct omap_dss_device *omap_dss_get_device(struct omap_dss_device *dssdev) | |
187 | { | |
188 | if (!try_module_get(dssdev->owner)) | |
189 | return NULL; | |
190 | ||
191 | if (get_device(dssdev->dev) == NULL) { | |
192 | module_put(dssdev->owner); | |
193 | return NULL; | |
194 | } | |
195 | ||
196 | return dssdev; | |
197 | } | |
198 | EXPORT_SYMBOL(omap_dss_get_device); | |
199 | ||
200 | void omap_dss_put_device(struct omap_dss_device *dssdev) | |
201 | { | |
202 | put_device(dssdev->dev); | |
203 | module_put(dssdev->owner); | |
204 | } | |
205 | EXPORT_SYMBOL(omap_dss_put_device); | |
206 | ||
207 | /* | |
208 | * ref count of the found device is incremented. | |
209 | * ref count of from-device is decremented. | |
210 | */ | |
211 | struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from) | |
212 | { | |
213 | struct list_head *l; | |
214 | struct omap_dss_device *dssdev; | |
215 | ||
216 | mutex_lock(&panel_list_mutex); | |
217 | ||
218 | if (list_empty(&panel_list)) { | |
219 | dssdev = NULL; | |
220 | goto out; | |
221 | } | |
222 | ||
223 | if (from == NULL) { | |
224 | dssdev = list_first_entry(&panel_list, struct omap_dss_device, | |
225 | panel_list); | |
226 | omap_dss_get_device(dssdev); | |
227 | goto out; | |
228 | } | |
229 | ||
230 | omap_dss_put_device(from); | |
231 | ||
232 | list_for_each(l, &panel_list) { | |
233 | dssdev = list_entry(l, struct omap_dss_device, panel_list); | |
234 | if (dssdev == from) { | |
235 | if (list_is_last(l, &panel_list)) { | |
236 | dssdev = NULL; | |
237 | goto out; | |
238 | } | |
239 | ||
240 | dssdev = list_entry(l->next, struct omap_dss_device, | |
241 | panel_list); | |
242 | omap_dss_get_device(dssdev); | |
243 | goto out; | |
244 | } | |
245 | } | |
246 | ||
247 | WARN(1, "'from' dssdev not found\n"); | |
248 | ||
249 | dssdev = NULL; | |
250 | out: | |
251 | mutex_unlock(&panel_list_mutex); | |
252 | return dssdev; | |
253 | } | |
254 | EXPORT_SYMBOL(omap_dss_get_next_device); | |
255 | ||
256 | struct omap_dss_device *omap_dss_find_device(void *data, | |
257 | int (*match)(struct omap_dss_device *dssdev, void *data)) | |
258 | { | |
259 | struct omap_dss_device *dssdev = NULL; | |
260 | ||
261 | while ((dssdev = omap_dss_get_next_device(dssdev)) != NULL) { | |
262 | if (match(dssdev, data)) | |
263 | return dssdev; | |
264 | } | |
265 | ||
266 | return NULL; | |
267 | } | |
268 | EXPORT_SYMBOL(omap_dss_find_device); | |
269 | ||
270 | void videomode_to_omap_video_timings(const struct videomode *vm, | |
271 | struct omap_video_timings *ovt) | |
272 | { | |
273 | memset(ovt, 0, sizeof(*ovt)); | |
274 | ||
275 | ovt->pixelclock = vm->pixelclock; | |
276 | ovt->x_res = vm->hactive; | |
277 | ovt->hbp = vm->hback_porch; | |
278 | ovt->hfp = vm->hfront_porch; | |
279 | ovt->hsw = vm->hsync_len; | |
280 | ovt->y_res = vm->vactive; | |
281 | ovt->vbp = vm->vback_porch; | |
282 | ovt->vfp = vm->vfront_porch; | |
283 | ovt->vsw = vm->vsync_len; | |
284 | ||
285 | ovt->vsync_level = vm->flags & DISPLAY_FLAGS_VSYNC_HIGH ? | |
286 | OMAPDSS_SIG_ACTIVE_HIGH : | |
287 | OMAPDSS_SIG_ACTIVE_LOW; | |
288 | ovt->hsync_level = vm->flags & DISPLAY_FLAGS_HSYNC_HIGH ? | |
289 | OMAPDSS_SIG_ACTIVE_HIGH : | |
290 | OMAPDSS_SIG_ACTIVE_LOW; | |
291 | ovt->de_level = vm->flags & DISPLAY_FLAGS_DE_HIGH ? | |
292 | OMAPDSS_SIG_ACTIVE_HIGH : | |
293 | OMAPDSS_SIG_ACTIVE_LOW; | |
294 | ovt->data_pclk_edge = vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE ? | |
295 | OMAPDSS_DRIVE_SIG_RISING_EDGE : | |
296 | OMAPDSS_DRIVE_SIG_FALLING_EDGE; | |
297 | ||
298 | ovt->sync_pclk_edge = ovt->data_pclk_edge; | |
299 | } | |
300 | EXPORT_SYMBOL(videomode_to_omap_video_timings); | |
301 | ||
302 | void omap_video_timings_to_videomode(const struct omap_video_timings *ovt, | |
303 | struct videomode *vm) | |
304 | { | |
305 | memset(vm, 0, sizeof(*vm)); | |
306 | ||
307 | vm->pixelclock = ovt->pixelclock; | |
308 | ||
309 | vm->hactive = ovt->x_res; | |
310 | vm->hback_porch = ovt->hbp; | |
311 | vm->hfront_porch = ovt->hfp; | |
312 | vm->hsync_len = ovt->hsw; | |
313 | vm->vactive = ovt->y_res; | |
314 | vm->vback_porch = ovt->vbp; | |
315 | vm->vfront_porch = ovt->vfp; | |
316 | vm->vsync_len = ovt->vsw; | |
317 | ||
318 | if (ovt->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH) | |
319 | vm->flags |= DISPLAY_FLAGS_HSYNC_HIGH; | |
320 | else | |
321 | vm->flags |= DISPLAY_FLAGS_HSYNC_LOW; | |
322 | ||
323 | if (ovt->vsync_level == OMAPDSS_SIG_ACTIVE_HIGH) | |
324 | vm->flags |= DISPLAY_FLAGS_VSYNC_HIGH; | |
325 | else | |
326 | vm->flags |= DISPLAY_FLAGS_VSYNC_LOW; | |
327 | ||
328 | if (ovt->de_level == OMAPDSS_SIG_ACTIVE_HIGH) | |
329 | vm->flags |= DISPLAY_FLAGS_DE_HIGH; | |
330 | else | |
331 | vm->flags |= DISPLAY_FLAGS_DE_LOW; | |
332 | ||
333 | if (ovt->data_pclk_edge == OMAPDSS_DRIVE_SIG_RISING_EDGE) | |
334 | vm->flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE; | |
335 | else | |
336 | vm->flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE; | |
337 | } | |
338 | EXPORT_SYMBOL(omap_video_timings_to_videomode); |