hdmi_panel.c 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  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. #include <linux/kernel.h>
  22. #include <linux/err.h>
  23. #include <linux/io.h>
  24. #include <linux/mutex.h>
  25. #include <linux/module.h>
  26. #include <video/omapdss.h>
  27. #include <linux/slab.h>
  28. #include "dss.h"
  29. static struct {
  30. /* This protects the panel ops, mainly when accessing the HDMI IP. */
  31. struct mutex lock;
  32. #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
  33. /* This protects the audio ops, specifically. */
  34. spinlock_t audio_lock;
  35. #endif
  36. } hdmi;
  37. static int hdmi_panel_probe(struct omap_dss_device *dssdev)
  38. {
  39. /* Initialize default timings to VGA in DVI mode */
  40. const struct omap_video_timings default_timings = {
  41. .x_res = 640,
  42. .y_res = 480,
  43. .pixel_clock = 25175,
  44. .hsw = 96,
  45. .hfp = 16,
  46. .hbp = 48,
  47. .vsw = 2,
  48. .vfp = 11,
  49. .vbp = 31,
  50. .vsync_level = OMAPDSS_SIG_ACTIVE_LOW,
  51. .hsync_level = OMAPDSS_SIG_ACTIVE_LOW,
  52. .interlace = false,
  53. };
  54. DSSDBG("ENTER hdmi_panel_probe\n");
  55. dssdev->panel.timings = default_timings;
  56. DSSDBG("hdmi_panel_probe x_res= %d y_res = %d\n",
  57. dssdev->panel.timings.x_res,
  58. dssdev->panel.timings.y_res);
  59. return 0;
  60. }
  61. static void hdmi_panel_remove(struct omap_dss_device *dssdev)
  62. {
  63. }
  64. #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
  65. static int hdmi_panel_audio_enable(struct omap_dss_device *dssdev)
  66. {
  67. unsigned long flags;
  68. int r;
  69. mutex_lock(&hdmi.lock);
  70. spin_lock_irqsave(&hdmi.audio_lock, flags);
  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. r = hdmi_audio_enable();
  79. if (!r)
  80. dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED;
  81. err:
  82. spin_unlock_irqrestore(&hdmi.audio_lock, flags);
  83. mutex_unlock(&hdmi.lock);
  84. return r;
  85. }
  86. static void hdmi_panel_audio_disable(struct omap_dss_device *dssdev)
  87. {
  88. unsigned long flags;
  89. spin_lock_irqsave(&hdmi.audio_lock, flags);
  90. hdmi_audio_disable();
  91. dssdev->audio_state = OMAP_DSS_AUDIO_DISABLED;
  92. spin_unlock_irqrestore(&hdmi.audio_lock, flags);
  93. }
  94. static int hdmi_panel_audio_start(struct omap_dss_device *dssdev)
  95. {
  96. unsigned long flags;
  97. int r;
  98. spin_lock_irqsave(&hdmi.audio_lock, flags);
  99. /*
  100. * No need to check the panel state. It was checked when trasitioning
  101. * to AUDIO_ENABLED.
  102. */
  103. if (dssdev->audio_state != OMAP_DSS_AUDIO_ENABLED) {
  104. DSSERR("audio start from invalid state\n");
  105. r = -EPERM;
  106. goto err;
  107. }
  108. r = hdmi_audio_start();
  109. if (!r)
  110. dssdev->audio_state = OMAP_DSS_AUDIO_PLAYING;
  111. err:
  112. spin_unlock_irqrestore(&hdmi.audio_lock, flags);
  113. return r;
  114. }
  115. static void hdmi_panel_audio_stop(struct omap_dss_device *dssdev)
  116. {
  117. unsigned long flags;
  118. spin_lock_irqsave(&hdmi.audio_lock, flags);
  119. hdmi_audio_stop();
  120. dssdev->audio_state = OMAP_DSS_AUDIO_ENABLED;
  121. spin_unlock_irqrestore(&hdmi.audio_lock, flags);
  122. }
  123. static bool hdmi_panel_audio_supported(struct omap_dss_device *dssdev)
  124. {
  125. bool r = false;
  126. mutex_lock(&hdmi.lock);
  127. if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE)
  128. goto err;
  129. if (!hdmi_mode_has_audio())
  130. goto err;
  131. r = true;
  132. err:
  133. mutex_unlock(&hdmi.lock);
  134. return r;
  135. }
  136. static int hdmi_panel_audio_config(struct omap_dss_device *dssdev,
  137. struct omap_dss_audio *audio)
  138. {
  139. unsigned long flags;
  140. int r;
  141. mutex_lock(&hdmi.lock);
  142. spin_lock_irqsave(&hdmi.audio_lock, flags);
  143. /* config audio only if the display is active and supports audio */
  144. if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE ||
  145. !hdmi_mode_has_audio()) {
  146. DSSERR("audio not supported or display is off\n");
  147. r = -EPERM;
  148. goto err;
  149. }
  150. r = hdmi_audio_config(audio);
  151. if (!r)
  152. dssdev->audio_state = OMAP_DSS_AUDIO_CONFIGURED;
  153. err:
  154. spin_unlock_irqrestore(&hdmi.audio_lock, flags);
  155. mutex_unlock(&hdmi.lock);
  156. return r;
  157. }
  158. #else
  159. static int hdmi_panel_audio_enable(struct omap_dss_device *dssdev)
  160. {
  161. return -EPERM;
  162. }
  163. static void hdmi_panel_audio_disable(struct omap_dss_device *dssdev)
  164. {
  165. }
  166. static int hdmi_panel_audio_start(struct omap_dss_device *dssdev)
  167. {
  168. return -EPERM;
  169. }
  170. static void hdmi_panel_audio_stop(struct omap_dss_device *dssdev)
  171. {
  172. }
  173. static bool hdmi_panel_audio_supported(struct omap_dss_device *dssdev)
  174. {
  175. return false;
  176. }
  177. static int hdmi_panel_audio_config(struct omap_dss_device *dssdev,
  178. struct omap_dss_audio *audio)
  179. {
  180. return -EPERM;
  181. }
  182. #endif
  183. static int hdmi_panel_enable(struct omap_dss_device *dssdev)
  184. {
  185. int r = 0;
  186. DSSDBG("ENTER hdmi_panel_enable\n");
  187. mutex_lock(&hdmi.lock);
  188. if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) {
  189. r = -EINVAL;
  190. goto err;
  191. }
  192. omapdss_hdmi_display_set_timing(dssdev, &dssdev->panel.timings);
  193. r = omapdss_hdmi_display_enable(dssdev);
  194. if (r) {
  195. DSSERR("failed to power on\n");
  196. goto err;
  197. }
  198. dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
  199. err:
  200. mutex_unlock(&hdmi.lock);
  201. return r;
  202. }
  203. static void hdmi_panel_disable(struct omap_dss_device *dssdev)
  204. {
  205. mutex_lock(&hdmi.lock);
  206. if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) {
  207. /*
  208. * TODO: notify audio users that the display was disabled. For
  209. * now, disable audio locally to not break our audio state
  210. * machine.
  211. */
  212. hdmi_panel_audio_disable(dssdev);
  213. omapdss_hdmi_display_disable(dssdev);
  214. }
  215. dssdev->state = OMAP_DSS_DISPLAY_DISABLED;
  216. mutex_unlock(&hdmi.lock);
  217. }
  218. static int hdmi_panel_suspend(struct omap_dss_device *dssdev)
  219. {
  220. int r = 0;
  221. mutex_lock(&hdmi.lock);
  222. if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
  223. r = -EINVAL;
  224. goto err;
  225. }
  226. /*
  227. * TODO: notify audio users that the display was suspended. For now,
  228. * disable audio locally to not break our audio state machine.
  229. */
  230. hdmi_panel_audio_disable(dssdev);
  231. dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED;
  232. omapdss_hdmi_display_disable(dssdev);
  233. err:
  234. mutex_unlock(&hdmi.lock);
  235. return r;
  236. }
  237. static int hdmi_panel_resume(struct omap_dss_device *dssdev)
  238. {
  239. int r = 0;
  240. mutex_lock(&hdmi.lock);
  241. if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) {
  242. r = -EINVAL;
  243. goto err;
  244. }
  245. r = omapdss_hdmi_display_enable(dssdev);
  246. if (r) {
  247. DSSERR("failed to power on\n");
  248. goto err;
  249. }
  250. /* TODO: notify audio users that the panel resumed. */
  251. dssdev->state = OMAP_DSS_DISPLAY_ACTIVE;
  252. err:
  253. mutex_unlock(&hdmi.lock);
  254. return r;
  255. }
  256. static void hdmi_get_timings(struct omap_dss_device *dssdev,
  257. struct omap_video_timings *timings)
  258. {
  259. mutex_lock(&hdmi.lock);
  260. *timings = dssdev->panel.timings;
  261. mutex_unlock(&hdmi.lock);
  262. }
  263. static void hdmi_set_timings(struct omap_dss_device *dssdev,
  264. struct omap_video_timings *timings)
  265. {
  266. DSSDBG("hdmi_set_timings\n");
  267. mutex_lock(&hdmi.lock);
  268. /*
  269. * TODO: notify audio users that there was a timings change. For
  270. * now, disable audio locally to not break our audio state machine.
  271. */
  272. hdmi_panel_audio_disable(dssdev);
  273. omapdss_hdmi_display_set_timing(dssdev, timings);
  274. dssdev->panel.timings = *timings;
  275. mutex_unlock(&hdmi.lock);
  276. }
  277. static int hdmi_check_timings(struct omap_dss_device *dssdev,
  278. struct omap_video_timings *timings)
  279. {
  280. int r = 0;
  281. DSSDBG("hdmi_check_timings\n");
  282. mutex_lock(&hdmi.lock);
  283. r = omapdss_hdmi_display_check_timing(dssdev, timings);
  284. mutex_unlock(&hdmi.lock);
  285. return r;
  286. }
  287. static int hdmi_read_edid(struct omap_dss_device *dssdev, u8 *buf, int len)
  288. {
  289. int r;
  290. mutex_lock(&hdmi.lock);
  291. if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
  292. r = omapdss_hdmi_display_enable(dssdev);
  293. if (r)
  294. goto err;
  295. }
  296. r = omapdss_hdmi_read_edid(buf, len);
  297. if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED ||
  298. dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
  299. omapdss_hdmi_display_disable(dssdev);
  300. err:
  301. mutex_unlock(&hdmi.lock);
  302. return r;
  303. }
  304. static bool hdmi_detect(struct omap_dss_device *dssdev)
  305. {
  306. int r;
  307. mutex_lock(&hdmi.lock);
  308. if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) {
  309. r = omapdss_hdmi_display_enable(dssdev);
  310. if (r)
  311. goto err;
  312. }
  313. r = omapdss_hdmi_detect();
  314. if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED ||
  315. dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED)
  316. omapdss_hdmi_display_disable(dssdev);
  317. err:
  318. mutex_unlock(&hdmi.lock);
  319. return r;
  320. }
  321. static struct omap_dss_driver hdmi_driver = {
  322. .probe = hdmi_panel_probe,
  323. .remove = hdmi_panel_remove,
  324. .enable = hdmi_panel_enable,
  325. .disable = hdmi_panel_disable,
  326. .suspend = hdmi_panel_suspend,
  327. .resume = hdmi_panel_resume,
  328. .get_timings = hdmi_get_timings,
  329. .set_timings = hdmi_set_timings,
  330. .check_timings = hdmi_check_timings,
  331. .read_edid = hdmi_read_edid,
  332. .detect = hdmi_detect,
  333. .audio_enable = hdmi_panel_audio_enable,
  334. .audio_disable = hdmi_panel_audio_disable,
  335. .audio_start = hdmi_panel_audio_start,
  336. .audio_stop = hdmi_panel_audio_stop,
  337. .audio_supported = hdmi_panel_audio_supported,
  338. .audio_config = hdmi_panel_audio_config,
  339. .driver = {
  340. .name = "hdmi_panel",
  341. .owner = THIS_MODULE,
  342. },
  343. };
  344. int hdmi_panel_init(void)
  345. {
  346. mutex_init(&hdmi.lock);
  347. #if defined(CONFIG_OMAP4_DSS_HDMI_AUDIO)
  348. spin_lock_init(&hdmi.audio_lock);
  349. #endif
  350. omap_dss_register_driver(&hdmi_driver);
  351. return 0;
  352. }
  353. void hdmi_panel_exit(void)
  354. {
  355. omap_dss_unregister_driver(&hdmi_driver);
  356. }