hdmi_panel.c 9.2 KB

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