Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.5 446 lines 9.4 kB view raw
1/* 2 * hdmi_panel.c 3 * 4 * HDMI library support functions for TI OMAP4 processors. 5 * 6 * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com/ 7 * Authors: Mythri P k <mythripk@ti.com> 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms of the GNU General Public License version 2 as published by 11 * the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it will be useful, but WITHOUT 14 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 15 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 16 * more details. 17 * 18 * You should have received a copy of the GNU General Public License along with 19 * this program. If not, see <http://www.gnu.org/licenses/>. 20 */ 21 22#include <linux/kernel.h> 23#include <linux/err.h> 24#include <linux/io.h> 25#include <linux/mutex.h> 26#include <linux/module.h> 27#include <video/omapdss.h> 28#include <linux/slab.h> 29 30#include "dss.h" 31 32static struct { 33 /* This protects the panel ops, mainly when accessing the HDMI IP. */ 34 struct mutex lock; 35#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) 36 /* This protects the audio ops, specifically. */ 37 spinlock_t audio_lock; 38#endif 39} hdmi; 40 41 42static int hdmi_panel_probe(struct omap_dss_device *dssdev) 43{ 44 DSSDBG("ENTER hdmi_panel_probe\n"); 45 46 dssdev->panel.config = OMAP_DSS_LCD_TFT | 47 OMAP_DSS_LCD_IVS | OMAP_DSS_LCD_IHS; 48 49 dssdev->panel.timings = (struct omap_video_timings){640, 480, 25175, 96, 16, 48, 2 , 11, 31}; 50 51 DSSDBG("hdmi_panel_probe x_res= %d y_res = %d\n", 52 dssdev->panel.timings.x_res, 53 dssdev->panel.timings.y_res); 54 return 0; 55} 56 57static void hdmi_panel_remove(struct omap_dss_device *dssdev) 58{ 59 60} 61 62#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) 63static int hdmi_panel_audio_enable(struct omap_dss_device *dssdev) 64{ 65 unsigned long flags; 66 int r; 67 68 mutex_lock(&hdmi.lock); 69 spin_lock_irqsave(&hdmi.audio_lock, flags); 70 71 /* enable audio only if the display is active and supports audio */ 72 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || 73 !hdmi_mode_has_audio()) { 74 DSSERR("audio not supported or display is off\n"); 75 r = -EPERM; 76 goto err; 77 } 78 79 r = hdmi_audio_enable(); 80 81 if (!r) 82 dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED; 83 84err: 85 spin_unlock_irqrestore(&hdmi.audio_lock, flags); 86 mutex_unlock(&hdmi.lock); 87 return r; 88} 89 90static void hdmi_panel_audio_disable(struct omap_dss_device *dssdev) 91{ 92 unsigned long flags; 93 94 spin_lock_irqsave(&hdmi.audio_lock, flags); 95 96 hdmi_audio_disable(); 97 98 dssdev->audio_state = OMAP_DSS_AUDIO_DISABLED; 99 100 spin_unlock_irqrestore(&hdmi.audio_lock, flags); 101} 102 103static int hdmi_panel_audio_start(struct omap_dss_device *dssdev) 104{ 105 unsigned long flags; 106 int r; 107 108 spin_lock_irqsave(&hdmi.audio_lock, flags); 109 /* 110 * No need to check the panel state. It was checked when trasitioning 111 * to AUDIO_ENABLED. 112 */ 113 if (dssdev->audio_state != OMAP_DSS_AUDIO_ENABLED) { 114 DSSERR("audio start from invalid state\n"); 115 r = -EPERM; 116 goto err; 117 } 118 119 r = hdmi_audio_start(); 120 121 if (!r) 122 dssdev->audio_state = OMAP_DSS_AUDIO_PLAYING; 123 124err: 125 spin_unlock_irqrestore(&hdmi.audio_lock, flags); 126 return r; 127} 128 129static void hdmi_panel_audio_stop(struct omap_dss_device *dssdev) 130{ 131 unsigned long flags; 132 133 spin_lock_irqsave(&hdmi.audio_lock, flags); 134 135 hdmi_audio_stop(); 136 dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED; 137 138 spin_unlock_irqrestore(&hdmi.audio_lock, flags); 139} 140 141static bool hdmi_panel_audio_supported(struct omap_dss_device *dssdev) 142{ 143 bool r = false; 144 145 mutex_lock(&hdmi.lock); 146 147 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) 148 goto err; 149 150 if (!hdmi_mode_has_audio()) 151 goto err; 152 153 r = true; 154err: 155 mutex_unlock(&hdmi.lock); 156 return r; 157} 158 159static int hdmi_panel_audio_config(struct omap_dss_device *dssdev, 160 struct omap_dss_audio *audio) 161{ 162 unsigned long flags; 163 int r; 164 165 mutex_lock(&hdmi.lock); 166 spin_lock_irqsave(&hdmi.audio_lock, flags); 167 168 /* config audio only if the display is active and supports audio */ 169 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE || 170 !hdmi_mode_has_audio()) { 171 DSSERR("audio not supported or display is off\n"); 172 r = -EPERM; 173 goto err; 174 } 175 176 r = hdmi_audio_config(audio); 177 178 if (!r) 179 dssdev->audio_state = OMAP_DSS_AUDIO_CONFIGURED; 180 181err: 182 spin_unlock_irqrestore(&hdmi.audio_lock, flags); 183 mutex_unlock(&hdmi.lock); 184 return r; 185} 186 187#else 188static int hdmi_panel_audio_enable(struct omap_dss_device *dssdev) 189{ 190 return -EPERM; 191} 192 193static void hdmi_panel_audio_disable(struct omap_dss_device *dssdev) 194{ 195} 196 197static int hdmi_panel_audio_start(struct omap_dss_device *dssdev) 198{ 199 return -EPERM; 200} 201 202static void hdmi_panel_audio_stop(struct omap_dss_device *dssdev) 203{ 204} 205 206static bool hdmi_panel_audio_supported(struct omap_dss_device *dssdev) 207{ 208 return false; 209} 210 211static int hdmi_panel_audio_config(struct omap_dss_device *dssdev, 212 struct omap_dss_audio *audio) 213{ 214 return -EPERM; 215} 216#endif 217 218static int hdmi_panel_enable(struct omap_dss_device *dssdev) 219{ 220 int r = 0; 221 DSSDBG("ENTER hdmi_panel_enable\n"); 222 223 mutex_lock(&hdmi.lock); 224 225 if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { 226 r = -EINVAL; 227 goto err; 228 } 229 230 r = omapdss_hdmi_display_enable(dssdev); 231 if (r) { 232 DSSERR("failed to power on\n"); 233 goto err; 234 } 235 236 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; 237 238err: 239 mutex_unlock(&hdmi.lock); 240 241 return r; 242} 243 244static void hdmi_panel_disable(struct omap_dss_device *dssdev) 245{ 246 mutex_lock(&hdmi.lock); 247 248 if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { 249 /* 250 * TODO: notify audio users that the display was disabled. For 251 * now, disable audio locally to not break our audio state 252 * machine. 253 */ 254 hdmi_panel_audio_disable(dssdev); 255 omapdss_hdmi_display_disable(dssdev); 256 } 257 258 dssdev->state = OMAP_DSS_DISPLAY_DISABLED; 259 260 mutex_unlock(&hdmi.lock); 261} 262 263static int hdmi_panel_suspend(struct omap_dss_device *dssdev) 264{ 265 int r = 0; 266 267 mutex_lock(&hdmi.lock); 268 269 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { 270 r = -EINVAL; 271 goto err; 272 } 273 274 /* 275 * TODO: notify audio users that the display was suspended. For now, 276 * disable audio locally to not break our audio state machine. 277 */ 278 hdmi_panel_audio_disable(dssdev); 279 280 dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; 281 omapdss_hdmi_display_disable(dssdev); 282 283err: 284 mutex_unlock(&hdmi.lock); 285 286 return r; 287} 288 289static int hdmi_panel_resume(struct omap_dss_device *dssdev) 290{ 291 int r = 0; 292 293 mutex_lock(&hdmi.lock); 294 295 if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) { 296 r = -EINVAL; 297 goto err; 298 } 299 300 r = omapdss_hdmi_display_enable(dssdev); 301 if (r) { 302 DSSERR("failed to power on\n"); 303 goto err; 304 } 305 /* TODO: notify audio users that the panel resumed. */ 306 307 dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; 308 309err: 310 mutex_unlock(&hdmi.lock); 311 312 return r; 313} 314 315static void hdmi_get_timings(struct omap_dss_device *dssdev, 316 struct omap_video_timings *timings) 317{ 318 mutex_lock(&hdmi.lock); 319 320 *timings = dssdev->panel.timings; 321 322 mutex_unlock(&hdmi.lock); 323} 324 325static void hdmi_set_timings(struct omap_dss_device *dssdev, 326 struct omap_video_timings *timings) 327{ 328 DSSDBG("hdmi_set_timings\n"); 329 330 mutex_lock(&hdmi.lock); 331 332 /* 333 * TODO: notify audio users that there was a timings change. For 334 * now, disable audio locally to not break our audio state machine. 335 */ 336 hdmi_panel_audio_disable(dssdev); 337 338 dssdev->panel.timings = *timings; 339 omapdss_hdmi_display_set_timing(dssdev); 340 341 mutex_unlock(&hdmi.lock); 342} 343 344static int hdmi_check_timings(struct omap_dss_device *dssdev, 345 struct omap_video_timings *timings) 346{ 347 int r = 0; 348 349 DSSDBG("hdmi_check_timings\n"); 350 351 mutex_lock(&hdmi.lock); 352 353 r = omapdss_hdmi_display_check_timing(dssdev, timings); 354 355 mutex_unlock(&hdmi.lock); 356 return r; 357} 358 359static int hdmi_read_edid(struct omap_dss_device *dssdev, u8 *buf, int len) 360{ 361 int r; 362 363 mutex_lock(&hdmi.lock); 364 365 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { 366 r = omapdss_hdmi_display_enable(dssdev); 367 if (r) 368 goto err; 369 } 370 371 r = omapdss_hdmi_read_edid(buf, len); 372 373 if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED || 374 dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) 375 omapdss_hdmi_display_disable(dssdev); 376err: 377 mutex_unlock(&hdmi.lock); 378 379 return r; 380} 381 382static bool hdmi_detect(struct omap_dss_device *dssdev) 383{ 384 int r; 385 386 mutex_lock(&hdmi.lock); 387 388 if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { 389 r = omapdss_hdmi_display_enable(dssdev); 390 if (r) 391 goto err; 392 } 393 394 r = omapdss_hdmi_detect(); 395 396 if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED || 397 dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) 398 omapdss_hdmi_display_disable(dssdev); 399err: 400 mutex_unlock(&hdmi.lock); 401 402 return r; 403} 404 405static struct omap_dss_driver hdmi_driver = { 406 .probe = hdmi_panel_probe, 407 .remove = hdmi_panel_remove, 408 .enable = hdmi_panel_enable, 409 .disable = hdmi_panel_disable, 410 .suspend = hdmi_panel_suspend, 411 .resume = hdmi_panel_resume, 412 .get_timings = hdmi_get_timings, 413 .set_timings = hdmi_set_timings, 414 .check_timings = hdmi_check_timings, 415 .read_edid = hdmi_read_edid, 416 .detect = hdmi_detect, 417 .audio_enable = hdmi_panel_audio_enable, 418 .audio_disable = hdmi_panel_audio_disable, 419 .audio_start = hdmi_panel_audio_start, 420 .audio_stop = hdmi_panel_audio_stop, 421 .audio_supported = hdmi_panel_audio_supported, 422 .audio_config = hdmi_panel_audio_config, 423 .driver = { 424 .name = "hdmi_panel", 425 .owner = THIS_MODULE, 426 }, 427}; 428 429int hdmi_panel_init(void) 430{ 431 mutex_init(&hdmi.lock); 432 433#if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO) 434 spin_lock_init(&hdmi.audio_lock); 435#endif 436 437 omap_dss_register_driver(&hdmi_driver); 438 439 return 0; 440} 441 442void hdmi_panel_exit(void) 443{ 444 omap_dss_unregister_driver(&hdmi_driver); 445 446}