hdmi_panel.c 9.2 KB

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