Commit | Line | Data |
---|---|---|
eed07e0e 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> | |
eed07e0e | 28 | #include <linux/platform_device.h> |
7f2bcd06 | 29 | #include <linux/of.h> |
eed07e0e | 30 | |
a0b38cc4 | 31 | #include <video/omapdss.h> |
eed07e0e | 32 | #include "dss.h" |
5ed8cf5b | 33 | #include "dss_features.h" |
eed07e0e | 34 | |
96adcece | 35 | void omapdss_default_get_resolution(struct omap_dss_device *dssdev, |
eed07e0e TV |
36 | u16 *xres, u16 *yres) |
37 | { | |
38 | *xres = dssdev->panel.timings.x_res; | |
39 | *yres = dssdev->panel.timings.y_res; | |
40 | } | |
96adcece | 41 | EXPORT_SYMBOL(omapdss_default_get_resolution); |
eed07e0e | 42 | |
a2699504 | 43 | int omapdss_default_get_recommended_bpp(struct omap_dss_device *dssdev) |
eed07e0e | 44 | { |
eed07e0e TV |
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: | |
eed07e0e TV |
53 | if (dssdev->ctrl.pixel_size == 24) |
54 | return 24; | |
55 | else | |
56 | return 16; | |
a3b3cc2b AT |
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; | |
eed07e0e TV |
62 | case OMAP_DISPLAY_TYPE_VENC: |
63 | case OMAP_DISPLAY_TYPE_SDI: | |
b119601d | 64 | case OMAP_DISPLAY_TYPE_HDMI: |
bc24b8b6 | 65 | case OMAP_DISPLAY_TYPE_DVI: |
eed07e0e | 66 | return 24; |
eed07e0e TV |
67 | default: |
68 | BUG(); | |
c6eee968 | 69 | return 0; |
eed07e0e TV |
70 | } |
71 | } | |
a2699504 | 72 | EXPORT_SYMBOL(omapdss_default_get_recommended_bpp); |
eed07e0e | 73 | |
4b6430fc GI |
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 | ||
eed07e0e TV |
81 | int dss_suspend_all_devices(void) |
82 | { | |
a65c8bda TV |
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 | } | |
eed07e0e TV |
95 | } |
96 | ||
97 | return 0; | |
98 | } | |
99 | ||
eed07e0e TV |
100 | int dss_resume_all_devices(void) |
101 | { | |
a65c8bda | 102 | struct omap_dss_device *dssdev = NULL; |
eed07e0e | 103 | |
a65c8bda TV |
104 | for_each_dss_dev(dssdev) { |
105 | if (!dssdev->driver) | |
106 | continue; | |
279fcd48 | 107 | |
a65c8bda TV |
108 | if (dssdev->activate_after_resume) { |
109 | dssdev->driver->enable(dssdev); | |
110 | dssdev->activate_after_resume = false; | |
111 | } | |
112 | } | |
279fcd48 | 113 | |
eed07e0e TV |
114 | return 0; |
115 | } | |
116 | ||
117 | void dss_disable_all_devices(void) | |
118 | { | |
a65c8bda TV |
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 | } | |
eed07e0e TV |
128 | } |
129 | ||
2e7e3dc7 TV |
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 | ||
138 | snprintf(dssdev->alias, sizeof(dssdev->alias), | |
139 | "display%d", disp_num_counter++); | |
140 | ||
7f2bcd06 TV |
141 | /* Use 'label' property for name, if it exists */ |
142 | if (dssdev->dev->of_node) | |
143 | of_property_read_string(dssdev->dev->of_node, "label", | |
144 | &dssdev->name); | |
145 | ||
146 | if (dssdev->name == NULL) | |
147 | dssdev->name = dssdev->alias; | |
148 | ||
2e7e3dc7 TV |
149 | if (drv && drv->get_resolution == NULL) |
150 | drv->get_resolution = omapdss_default_get_resolution; | |
151 | if (drv && drv->get_recommended_bpp == NULL) | |
152 | drv->get_recommended_bpp = omapdss_default_get_recommended_bpp; | |
153 | if (drv && drv->get_timings == NULL) | |
154 | drv->get_timings = omapdss_default_get_timings; | |
155 | ||
156 | mutex_lock(&panel_list_mutex); | |
157 | list_add_tail(&dssdev->panel_list, &panel_list); | |
158 | mutex_unlock(&panel_list_mutex); | |
159 | return 0; | |
160 | } | |
161 | EXPORT_SYMBOL(omapdss_register_display); | |
162 | ||
163 | void omapdss_unregister_display(struct omap_dss_device *dssdev) | |
164 | { | |
165 | mutex_lock(&panel_list_mutex); | |
166 | list_del(&dssdev->panel_list); | |
167 | mutex_unlock(&panel_list_mutex); | |
168 | } | |
169 | EXPORT_SYMBOL(omapdss_unregister_display); | |
eed07e0e | 170 | |
d35317a4 | 171 | struct omap_dss_device *omap_dss_get_device(struct omap_dss_device *dssdev) |
eed07e0e | 172 | { |
d35317a4 TV |
173 | if (!try_module_get(dssdev->owner)) |
174 | return NULL; | |
175 | ||
176 | if (get_device(dssdev->dev) == NULL) { | |
177 | module_put(dssdev->owner); | |
178 | return NULL; | |
179 | } | |
180 | ||
181 | return dssdev; | |
eed07e0e TV |
182 | } |
183 | EXPORT_SYMBOL(omap_dss_get_device); | |
184 | ||
185 | void omap_dss_put_device(struct omap_dss_device *dssdev) | |
186 | { | |
ecc8b370 | 187 | put_device(dssdev->dev); |
d35317a4 | 188 | module_put(dssdev->owner); |
eed07e0e TV |
189 | } |
190 | EXPORT_SYMBOL(omap_dss_put_device); | |
191 | ||
67b23ca1 TV |
192 | /* |
193 | * ref count of the found device is incremented. | |
194 | * ref count of from-device is decremented. | |
195 | */ | |
eed07e0e TV |
196 | struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from) |
197 | { | |
67b23ca1 TV |
198 | struct list_head *l; |
199 | struct omap_dss_device *dssdev; | |
200 | ||
201 | mutex_lock(&panel_list_mutex); | |
eed07e0e | 202 | |
67b23ca1 TV |
203 | if (list_empty(&panel_list)) { |
204 | dssdev = NULL; | |
205 | goto out; | |
eed07e0e TV |
206 | } |
207 | ||
67b23ca1 TV |
208 | if (from == NULL) { |
209 | dssdev = list_first_entry(&panel_list, struct omap_dss_device, | |
210 | panel_list); | |
211 | omap_dss_get_device(dssdev); | |
212 | goto out; | |
213 | } | |
214 | ||
215 | omap_dss_put_device(from); | |
216 | ||
217 | list_for_each(l, &panel_list) { | |
218 | dssdev = list_entry(l, struct omap_dss_device, panel_list); | |
219 | if (dssdev == from) { | |
220 | if (list_is_last(l, &panel_list)) { | |
221 | dssdev = NULL; | |
222 | goto out; | |
223 | } | |
224 | ||
225 | dssdev = list_entry(l->next, struct omap_dss_device, | |
226 | panel_list); | |
227 | omap_dss_get_device(dssdev); | |
228 | goto out; | |
229 | } | |
230 | } | |
eed07e0e | 231 | |
67b23ca1 TV |
232 | WARN(1, "'from' dssdev not found\n"); |
233 | ||
234 | dssdev = NULL; | |
235 | out: | |
236 | mutex_unlock(&panel_list_mutex); | |
eed07e0e TV |
237 | return dssdev; |
238 | } | |
239 | EXPORT_SYMBOL(omap_dss_get_next_device); | |
240 | ||
241 | struct omap_dss_device *omap_dss_find_device(void *data, | |
242 | int (*match)(struct omap_dss_device *dssdev, void *data)) | |
243 | { | |
244 | struct omap_dss_device *dssdev = NULL; | |
245 | ||
246 | while ((dssdev = omap_dss_get_next_device(dssdev)) != NULL) { | |
247 | if (match(dssdev, data)) | |
248 | return dssdev; | |
249 | } | |
250 | ||
251 | return NULL; | |
252 | } | |
253 | EXPORT_SYMBOL(omap_dss_find_device); | |
254 | ||
6fcd485b TV |
255 | void videomode_to_omap_video_timings(const struct videomode *vm, |
256 | struct omap_video_timings *ovt) | |
257 | { | |
258 | memset(ovt, 0, sizeof(*ovt)); | |
259 | ||
260 | ovt->pixel_clock = vm->pixelclock / 1000; | |
261 | ovt->x_res = vm->hactive; | |
262 | ovt->hbp = vm->hback_porch; | |
263 | ovt->hfp = vm->hfront_porch; | |
264 | ovt->hsw = vm->hsync_len; | |
265 | ovt->y_res = vm->vactive; | |
266 | ovt->vbp = vm->vback_porch; | |
267 | ovt->vfp = vm->vfront_porch; | |
268 | ovt->vsw = vm->vsync_len; | |
269 | ||
270 | ovt->vsync_level = vm->flags & DISPLAY_FLAGS_VSYNC_HIGH ? | |
271 | OMAPDSS_SIG_ACTIVE_HIGH : | |
272 | OMAPDSS_SIG_ACTIVE_LOW; | |
273 | ovt->hsync_level = vm->flags & DISPLAY_FLAGS_HSYNC_HIGH ? | |
274 | OMAPDSS_SIG_ACTIVE_HIGH : | |
275 | OMAPDSS_SIG_ACTIVE_LOW; | |
276 | ovt->de_level = vm->flags & DISPLAY_FLAGS_DE_HIGH ? | |
277 | OMAPDSS_SIG_ACTIVE_HIGH : | |
9b842a47 | 278 | OMAPDSS_SIG_ACTIVE_LOW; |
6fcd485b TV |
279 | ovt->data_pclk_edge = vm->flags & DISPLAY_FLAGS_PIXDATA_POSEDGE ? |
280 | OMAPDSS_DRIVE_SIG_RISING_EDGE : | |
281 | OMAPDSS_DRIVE_SIG_FALLING_EDGE; | |
282 | ||
283 | ovt->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES; | |
284 | } | |
285 | EXPORT_SYMBOL(videomode_to_omap_video_timings); | |
286 | ||
287 | void omap_video_timings_to_videomode(const struct omap_video_timings *ovt, | |
288 | struct videomode *vm) | |
289 | { | |
290 | memset(vm, 0, sizeof(*vm)); | |
291 | ||
292 | vm->pixelclock = ovt->pixel_clock * 1000; | |
293 | ||
294 | vm->hactive = ovt->x_res; | |
295 | vm->hback_porch = ovt->hbp; | |
296 | vm->hfront_porch = ovt->hfp; | |
297 | vm->hsync_len = ovt->hsw; | |
298 | vm->vactive = ovt->y_res; | |
299 | vm->vback_porch = ovt->vbp; | |
300 | vm->vfront_porch = ovt->vfp; | |
301 | vm->vsync_len = ovt->vsw; | |
302 | ||
303 | if (ovt->hsync_level == OMAPDSS_SIG_ACTIVE_HIGH) | |
304 | vm->flags |= DISPLAY_FLAGS_HSYNC_HIGH; | |
305 | else | |
306 | vm->flags |= DISPLAY_FLAGS_HSYNC_LOW; | |
307 | ||
308 | if (ovt->vsync_level == OMAPDSS_SIG_ACTIVE_HIGH) | |
309 | vm->flags |= DISPLAY_FLAGS_VSYNC_HIGH; | |
310 | else | |
311 | vm->flags |= DISPLAY_FLAGS_VSYNC_LOW; | |
312 | ||
313 | if (ovt->de_level == OMAPDSS_SIG_ACTIVE_HIGH) | |
314 | vm->flags |= DISPLAY_FLAGS_DE_HIGH; | |
315 | else | |
316 | vm->flags |= DISPLAY_FLAGS_DE_LOW; | |
317 | ||
318 | if (ovt->data_pclk_edge == OMAPDSS_DRIVE_SIG_RISING_EDGE) | |
319 | vm->flags |= DISPLAY_FLAGS_PIXDATA_POSEDGE; | |
320 | else | |
321 | vm->flags |= DISPLAY_FLAGS_PIXDATA_NEGEDGE; | |
322 | } | |
323 | EXPORT_SYMBOL(omap_video_timings_to_videomode); |