tda19988.c 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823
  1. /****************************************************************************
  2. * drivers/lcd/tda19988.c
  3. *
  4. * Copyright (C) 2019 Gregory Nutt. All rights reserved.
  5. * Author: Gregory Nutt <gnutt@nuttx.org>
  6. *
  7. * Derives rather loosely from the FreeBSD driver which has a compatible
  8. * two-clause BSD license:
  9. *
  10. * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
  11. * All rights reserved.
  12. *
  13. * Redistribution and use in source and binary forms, with or without
  14. * modification, are permitted provided that the following conditions
  15. * are met:
  16. *
  17. * 1. Redistributions of source code must retain the above copyright
  18. * notice, this list of conditions and the following disclaimer.
  19. * 2. Redistributions in binary form must reproduce the above copyright
  20. * notice, this list of conditions and the following disclaimer in
  21. * the documentation and/or other materials provided with the
  22. * distribution.
  23. * 3. Neither the name NuttX nor the names of its contributors may be
  24. * used to endorse or promote products derived from this software
  25. * without specific prior written permission.
  26. *
  27. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  28. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  29. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  30. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  31. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  32. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  33. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  34. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  35. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  36. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  37. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  38. * POSSIBILITY OF SUCH DAMAGE.
  39. *
  40. ****************************************************************************/
  41. /****************************************************************************
  42. * Included Files
  43. ****************************************************************************/
  44. #include <nuttx/config.h>
  45. #include <sys/types.h>
  46. #include <stdbool.h>
  47. #include <string.h>
  48. #include <poll.h>
  49. #include <assert.h>
  50. #include <errno.h>
  51. #include <nuttx/kmalloc.h>
  52. #include <nuttx/semaphore.h>
  53. #include <nuttx/fs/fs.h>
  54. #include <nuttx/drivers/drivers.h>
  55. #include <nuttx/video/edid.h>
  56. #include <nuttx/lcd/tda19988.h>
  57. #include "tda19988.h"
  58. /****************************************************************************
  59. * Pre-processor Definitions
  60. ****************************************************************************/
  61. /* Returned values from tda19988_connected() */
  62. #define DISPLAY_CONNECTED 0
  63. #define DISPLAY_DETACHED 1
  64. /* Number of times to try reading EDID */
  65. #define MAX_READ_ATTEMPTS 100
  66. #define HDMI_CTRL_CEC_ENAMODS 0xff
  67. /****************************************************************************
  68. * Private Types
  69. ****************************************************************************/
  70. /* This structure represents the state of one TDA19988 driver instance */
  71. struct tda1988_dev_s
  72. {
  73. /* The contained lower half driver instance */
  74. FAR const struct tda19988_lower_s *lower;
  75. /* Upper half driver state */
  76. sem_t exclsem; /* Assures exclusive access to the driver */
  77. uint8_t page; /* Currently selected page */
  78. uint8_t crefs; /* Number of open references */
  79. #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
  80. bool unlinked; /* True, driver has been unlinked */
  81. #endif
  82. uint16_t version; /* TDA19988 version */
  83. FAR uint8_t *edid; /* Extended Display Identification Data */
  84. uint32_t edid_len; /* Size of EDID */
  85. };
  86. /****************************************************************************
  87. * Private Function Prototypes
  88. ****************************************************************************/
  89. /* General I2C Helpers */
  90. static int tda19988_getregs(FAR const struct tda19988_i2c_s *dev,
  91. uint8_t regaddr, FAR uint8_t *regval, int nregs);
  92. static int tda19988_putreg(FAR const struct tda19988_i2c_s *dev,
  93. uint8_t regaddr, uint8_t regval);
  94. static int tda19988_putreg16(FAR const struct tda19988_i2c_s *dev,
  95. uint8_t regaddr, uint16_t regval);
  96. static int tda19988_modifyreg(FAR const struct tda19988_i2c_s *dev,
  97. uint8_t regaddr, uint8_t clrbits, uint8_t setbits);
  98. /* CEC I2C Helpers */
  99. static inline int tda19988_cec_getregs(FAR struct tda1988_dev_s *priv,
  100. uint8_t regaddr, FAR uint8_t *regval, int nregs);
  101. static inline int tda19988_cec_putreg(FAR struct tda1988_dev_s *priv,
  102. uint8_t regaddr, uint8_t regval);
  103. static inline int tda19988_cec_modifyreg(FAR struct tda1988_dev_s *priv,
  104. uint8_t regaddr, uint8_t clrbits, uint8_t setbits);
  105. /* HDMI I2C Helpers */
  106. static int tda19988_select_page(FAR struct tda1988_dev_s *priv,
  107. uint8_t page);
  108. static int tda19988_hdmi_getregs(FAR struct tda1988_dev_s *priv,
  109. uint16_t reginfo, FAR uint8_t *regval, int nregs);
  110. static int tda19988_hdmi_putreg(FAR struct tda1988_dev_s *priv,
  111. uint16_t reginfo, uint8_t regval);
  112. static int tda19988_hdmi_putreg16(FAR struct tda1988_dev_s *priv,
  113. uint16_t reginfo, uint16_t regval);
  114. static int tda19988_hdmi_modifyreg(FAR struct tda1988_dev_s *priv,
  115. uint16_t reginfo, uint8_t clrbits, uint8_t setbits);
  116. /* CEC Module Helpers */
  117. #if 0 /* Not used */
  118. static int tda19988_connected(FAR struct tda1988_dev_s *priv);
  119. #endif
  120. /* HDMI Module Helpers */
  121. static int tda19988_fetch_edid_block(FAR struct tda1988_dev_s *priv,
  122. FAR uint8_t *buf, int block);
  123. static int tda19988_fetch_edid(struct tda1988_dev_s *priv);
  124. static ssize_t tda19988_read_internal(FAR struct tda1988_dev_s *priv,
  125. off_t offset, FAR uint8_t *buffer, size_t buflen);
  126. /* Character driver methods */
  127. static int tda19988_open(FAR struct file *filep);
  128. static int tda19988_close(FAR struct file *filep);
  129. static ssize_t tda19988_read(FAR struct file *filep, FAR char *buffer,
  130. size_t buflen);
  131. static ssize_t tda19988_write(FAR struct file *filep, FAR const char *buffer,
  132. size_t buflen);
  133. static off_t tda19988_seek(FAR struct file *filep, off_t offset,
  134. int whence);
  135. static int tda19988_ioctl(FAR struct file *filep, int cmd,
  136. unsigned long arg);
  137. static int tda19988_poll(FAR struct file *filep, FAR struct pollfd *fds,
  138. bool setup);
  139. #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
  140. static int tda19988_unlink(FAR struct inode *inode);
  141. #endif
  142. /* Initialization */
  143. static int tda19988_hwinitialize(FAR struct tda1988_dev_s *priv);
  144. static int tda19988_videomode_internal(FAR struct tda1988_dev_s *priv,
  145. FAR const struct videomode_s *mode);
  146. #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
  147. static void tda19988_shutdown(FAR struct tda1988_dev_s *priv);
  148. #endif
  149. /****************************************************************************
  150. * Private Data
  151. ****************************************************************************/
  152. static const struct file_operations tda19988_fops =
  153. {
  154. tda19988_open, /* open */
  155. tda19988_close, /* close */
  156. tda19988_read, /* read */
  157. tda19988_write, /* write */
  158. tda19988_seek, /* seek */
  159. tda19988_ioctl, /* ioctl */
  160. tda19988_poll /* poll */
  161. #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
  162. , tda19988_unlink /* unlink */
  163. #endif
  164. };
  165. /****************************************************************************
  166. * Private Functions
  167. ****************************************************************************/
  168. /****************************************************************************
  169. * Name: tda19988_getregs
  170. *
  171. * Description:
  172. * Read the value from one or more TDA19988 CEC or HDMI registers
  173. *
  174. * Returned Value:
  175. * Zero (OK) is returned on success; otherwise a negated errno value is
  176. * returned.
  177. *
  178. ****************************************************************************/
  179. static int tda19988_getregs(FAR const struct tda19988_i2c_s *dev,
  180. uint8_t regaddr, FAR uint8_t *regval, int nregs)
  181. {
  182. uint8_t buffer[1];
  183. int ret;
  184. DEBUGASSERT(dev != NULL && regval != NULL && nregs > 0);
  185. /* Write the register address and read the register value */
  186. buffer[0] = regaddr;
  187. ret = i2c_writeread(dev->i2c, &dev->config, buffer, 1, regval, nregs);
  188. if (ret < 0)
  189. {
  190. lcderr("ERROR: i2c_writeread() failed: %d\n", ret);
  191. return -1;
  192. }
  193. lcdinfo("Write: %02x<-%02x\n", regaddr, *regval);
  194. lcderrdumpbuffer("Read:", regval, nregs);
  195. return OK;
  196. }
  197. /****************************************************************************
  198. * Name: tda19988_putreg
  199. *
  200. * Description:
  201. * Write an 8-bit value to one TDA19988 CEC or HDMI register
  202. *
  203. * Returned Value:
  204. * Zero (OK) is returned on success; otherwise a negated errno value is
  205. * returned.
  206. *
  207. ****************************************************************************/
  208. static int tda19988_putreg(FAR const struct tda19988_i2c_s *dev,
  209. uint8_t regaddr, uint8_t regval)
  210. {
  211. uint8_t buffer[2];
  212. int ret;
  213. /* Write the register address and the register value */
  214. buffer[0] = regaddr;
  215. buffer[1] = regval;
  216. ret = i2c_write(dev->i2c, &dev->config, buffer, 2);
  217. if (ret < 0)
  218. {
  219. lcderr("ERROR: i2c_write() failed: %d\n", ret);
  220. return ret;
  221. }
  222. lcdinfo("Wrote: %02x<-%02x\n", regaddr, regval);
  223. return OK;
  224. }
  225. /****************************************************************************
  226. * Name: tda19988_putreg16
  227. *
  228. * Description:
  229. * Write a 16-bit value to one TDA19988 CEC or HDMI register
  230. *
  231. * Returned Value:
  232. * Zero (OK) is returned on success; otherwise a negated errno value is
  233. * returned.
  234. *
  235. ****************************************************************************/
  236. static int tda19988_putreg16(FAR const struct tda19988_i2c_s *dev,
  237. uint8_t regaddr, uint16_t regval)
  238. {
  239. uint8_t buffer[3];
  240. int ret;
  241. /* Write the register address and the register value */
  242. buffer[0] = regaddr;
  243. buffer[1] = (regval >> 8);
  244. buffer[2] = (regval & 0xff);
  245. ret = i2c_write(dev->i2c, &dev->config, buffer, 3);
  246. if (ret < 0)
  247. {
  248. lcderr("ERROR: i2c_write() failed: %d\n", ret);
  249. return ret;
  250. }
  251. lcdinfo("Wrote: 02x<-%04x\n", regaddr, regval);
  252. return OK;
  253. }
  254. /****************************************************************************
  255. * Name: tda19988_modifyreg
  256. *
  257. * Description:
  258. * Modify bits in one TDA19988 CEC or HDMI register
  259. *
  260. * Returned Value:
  261. * Zero (OK) is returned on success; otherwise a negated errno value is
  262. * returned.
  263. *
  264. ****************************************************************************/
  265. static int tda19988_modifyreg(FAR const struct tda19988_i2c_s *dev,
  266. uint8_t regaddr, uint8_t clrbits,
  267. uint8_t setbits)
  268. {
  269. uint8_t regval;
  270. int ret;
  271. /* Read the register contents */
  272. ret = tda19988_getregs(dev, regaddr, &regval, 1);
  273. if (ret < 0)
  274. {
  275. lcderr("ERROR: tda19988_getregs failed: %d\n", ret);
  276. return ret;
  277. }
  278. /* Modify the register content */
  279. regval &= ~clrbits;
  280. regval |= setbits;
  281. /* Write the modified register content */
  282. ret = tda19988_putreg(dev, regaddr, regval);
  283. if (ret < 0)
  284. {
  285. lcderr("ERROR: tda19988_putreg failed: %d\n", ret);
  286. return ret;
  287. }
  288. return OK;
  289. }
  290. /****************************************************************************
  291. * Name: tda19988_cec_getregs
  292. *
  293. * Description:
  294. * Read the value from one or more TDA19988 CEC registers
  295. *
  296. * Returned Value:
  297. * Zero (OK) is returned on success; otherwise a negated errno value is
  298. * returned.
  299. *
  300. ****************************************************************************/
  301. static inline int tda19988_cec_getregs(FAR struct tda1988_dev_s *priv,
  302. uint8_t regaddr, FAR uint8_t *regval,
  303. int nregs)
  304. {
  305. return tda19988_getregs(&priv->lower->cec, regaddr, regval, nregs);
  306. }
  307. /****************************************************************************
  308. * Name: tda19988_cec_putreg
  309. *
  310. * Description:
  311. * Write a value to one TDA19988 CEC register
  312. *
  313. * Returned Value:
  314. * Zero (OK) is returned on success; otherwise a negated errno value is
  315. * returned.
  316. *
  317. ****************************************************************************/
  318. static inline int tda19988_cec_putreg(FAR struct tda1988_dev_s *priv,
  319. uint8_t regaddr, uint8_t regval)
  320. {
  321. return tda19988_putreg(&priv->lower->cec, regaddr, regval);
  322. }
  323. /****************************************************************************
  324. * Name: tda19988_cec_modifyreg
  325. *
  326. * Description:
  327. * Modify bits in one TDA19988 CEC register
  328. *
  329. * Returned Value:
  330. * Zero (OK) is returned on success; otherwise a negated errno value is
  331. * returned.
  332. *
  333. ****************************************************************************/
  334. static inline int tda19988_cec_modifyreg(FAR struct tda1988_dev_s *priv,
  335. uint8_t regaddr, uint8_t clrbits,
  336. uint8_t setbits)
  337. {
  338. return tda19988_modifyreg(&priv->lower->cec, regaddr, clrbits, setbits);
  339. }
  340. /****************************************************************************
  341. * Name: tda19988_select_page
  342. *
  343. * Description:
  344. * Select the HDMI page (if not already selected)
  345. *
  346. * Returned Value:
  347. * Zero (OK) is returned on success; otherwise a negated errno value is
  348. * returned.
  349. *
  350. ****************************************************************************/
  351. static int tda19988_select_page(FAR struct tda1988_dev_s *priv, uint8_t page)
  352. {
  353. int ret = OK;
  354. /* Check if we need to select a new page for this transfer */
  355. if (page != HDMI_NO_PAGE && page != priv->page)
  356. {
  357. ret = tda19988_putreg(&priv->lower->hdmi,
  358. REGADDR(HDMI_PAGE_SELECT_REG), page);
  359. }
  360. return ret;
  361. }
  362. /****************************************************************************
  363. * Name: tda19988_hdmi_getregs
  364. *
  365. * Description:
  366. * Read the value from one or more TDA19988 HDMI registers
  367. *
  368. * Returned Value:
  369. * Zero (OK) is returned on success; otherwise a negated errno value is
  370. * returned.
  371. *
  372. ****************************************************************************/
  373. static int tda19988_hdmi_getregs(FAR struct tda1988_dev_s *priv,
  374. uint16_t reginfo, FAR uint8_t *regval,
  375. int nregs)
  376. {
  377. uint8_t page = REGPAGE(reginfo);
  378. uint8_t regaddr = REGADDR(reginfo);
  379. int ret;
  380. DEBUGASSERT(priv != NULL && regval != NULL && nregs > 0);
  381. /* Select the HDMI page */
  382. ret = tda19988_select_page(priv, page);
  383. if (ret < 0)
  384. {
  385. lcderr("ERROR: Failed to select page %02x: %d\n", page, ret);
  386. return ret;
  387. }
  388. /* Write the register address and read the register value */
  389. ret = tda19988_getregs(&priv->lower->hdmi, regaddr, regval, nregs);
  390. if (ret < 0)
  391. {
  392. lcderr("ERROR: tda19988_getregs() failed: %d\n", ret);
  393. return -1;
  394. }
  395. lcdinfo("Read: %02x:%02x->%02x\n", page, regaddr, *regval);
  396. return OK;
  397. }
  398. /****************************************************************************
  399. * Name: tda19988_hdmi_putreg
  400. *
  401. * Description:
  402. * Write an 8-bit value to one TDA19988 HDMI register
  403. *
  404. * Returned Value:
  405. * Zero (OK) is returned on success; otherwise a negated errno value is
  406. * returned.
  407. *
  408. ****************************************************************************/
  409. static int tda19988_hdmi_putreg(FAR struct tda1988_dev_s *priv,
  410. uint16_t reginfo, uint8_t regval)
  411. {
  412. uint8_t page = REGPAGE(reginfo);
  413. uint8_t regaddr = REGADDR(reginfo);
  414. int ret;
  415. /* Select the HDMI page */
  416. ret = tda19988_select_page(priv, page);
  417. if (ret < 0)
  418. {
  419. lcderr("ERROR: tda19988_select_page failed page %02x: %d\n",
  420. page, ret);
  421. return ret;
  422. }
  423. /* Write the register address and the register value */
  424. ret = tda19988_putreg(&priv->lower->hdmi, regaddr, regval);
  425. if (ret < 0)
  426. {
  427. lcderr("ERROR: tda19988_putreg() failed: %d\n", ret);
  428. return ret;
  429. }
  430. lcdinfo("Read: %02x:%02x<-%02x\n", page, regaddr, regval);
  431. return OK;
  432. }
  433. /****************************************************************************
  434. * Name: tda19988_hdmi_putreg16
  435. *
  436. * Description:
  437. * Write a 16-bit value to one TDA19988 HDMI register
  438. *
  439. * Returned Value:
  440. * Zero (OK) is returned on success; otherwise a negated errno value is
  441. * returned.
  442. *
  443. ****************************************************************************/
  444. static int tda19988_hdmi_putreg16(FAR struct tda1988_dev_s *priv,
  445. uint16_t reginfo, uint16_t regval)
  446. {
  447. uint8_t page = REGPAGE(reginfo);
  448. uint8_t regaddr = REGADDR(reginfo);
  449. int ret;
  450. /* Select the HDMI page */
  451. ret = tda19988_select_page(priv, page);
  452. if (ret < 0)
  453. {
  454. lcderr("ERROR: tda19988_select_page failed page %02x: %d\n",
  455. page, ret);
  456. return ret;
  457. }
  458. /* Write the register address and the register value */
  459. ret = tda19988_putreg16(&priv->lower->hdmi, regaddr, regval);
  460. if (ret < 0)
  461. {
  462. lcderr("ERROR: tda19988_putreg16() failed: %d\n", ret);
  463. return ret;
  464. }
  465. lcdinfo("Read: %02x:%02x<-%04x\n", page, regaddr, regval);
  466. return OK;
  467. }
  468. /****************************************************************************
  469. * Name: tda19988_hdmi_modifyreg
  470. *
  471. * Description:
  472. * Modify bits in one TDA19988 HDMI register
  473. *
  474. * Returned Value:
  475. * Zero (OK) is returned on success; otherwise a negated errno value is
  476. * returned.
  477. *
  478. ****************************************************************************/
  479. static int tda19988_hdmi_modifyreg(FAR struct tda1988_dev_s *priv,
  480. uint16_t reginfo, uint8_t clrbits,
  481. uint8_t setbits)
  482. {
  483. uint8_t page = REGPAGE(reginfo);
  484. uint8_t regaddr = REGADDR(reginfo);
  485. int ret;
  486. /* Select the HDMI page */
  487. ret = tda19988_select_page(priv, page);
  488. if (ret < 0)
  489. {
  490. lcderr("ERROR: Failed to select page %02x: %d\n", page, ret);
  491. return ret;
  492. }
  493. /* Read-modify-write the register contents */
  494. ret = tda19988_modifyreg(&priv->lower->hdmi, regaddr, clrbits, setbits);
  495. if (ret < 0)
  496. {
  497. lcderr("ERROR: tda19988_modifyreg failed: %d\n", ret);
  498. return ret;
  499. }
  500. return OK;
  501. }
  502. /****************************************************************************
  503. * Name: tda19988_connected
  504. *
  505. * Description:
  506. * Check if a display is connected.
  507. *
  508. * Returned Values:
  509. * DISPLAY_CONNECTED - A display is connected
  510. * DISPLAY_DETACHED - No display is connected
  511. * A negated errno value is returned on any failure.
  512. *
  513. ****************************************************************************/
  514. #if 0 /* Not used */
  515. static int tda19988_connected(FAR struct tda1988_dev_s *priv)
  516. {
  517. uint8_t regval;
  518. int ret;
  519. ret = tda19988_cec_getregs(priv, CEC_STATUS_REG, &regval, 1);
  520. if (ret < 0)
  521. {
  522. lcderr("ERROR: tda19988_cec_getregs failed: %d\n", ret);
  523. return ret;
  524. }
  525. if ((regval & CEC_STATUS_CONNECTED) == 0)
  526. {
  527. lcdwarn("WARNING: Display not connected\n");
  528. return DISPLAY_DETACHED;
  529. }
  530. else
  531. {
  532. lcdinfo("Display connect\n");
  533. return DISPLAY_CONNECTED;
  534. }
  535. }
  536. #endif
  537. /****************************************************************************
  538. * Name: tda19988_fetch_edid_block
  539. *
  540. * Description:
  541. * Fetch one EDID block from the DSD.
  542. *
  543. * Returned Value:
  544. * Zero (OK) is returned on success; A negated errno value is returned on
  545. * any failure.
  546. *
  547. ****************************************************************************/
  548. static int tda19988_fetch_edid_block(FAR struct tda1988_dev_s *priv,
  549. FAR uint8_t *buf, int block)
  550. {
  551. uint8_t data;
  552. int attempt;
  553. int ret;
  554. ret = OK;
  555. tda19988_hdmi_modifyreg(priv, HDMI_CTRL_INT_REG, 0, HDMI_CTRL_INT_EDID);
  556. /* Block 0 */
  557. tda19988_hdmi_putreg(priv, HDMI_EDID_DEV_ADDR_REG, 0xa0);
  558. tda19988_hdmi_putreg(priv, HDMI_EDID_OFFSET_REG,
  559. (block & 1) != 0 ? 128 : 0);
  560. tda19988_hdmi_putreg(priv, HDMI_EDID_SEGM_ADDR_REG, 0x60);
  561. tda19988_hdmi_putreg(priv, HDMI_EDID_DDC_SEGM_REG, block >> 1);
  562. tda19988_hdmi_putreg(priv, HDMI_EDID_REQ_REG, HDMI_EDID_REQ_READ);
  563. tda19988_hdmi_putreg(priv, HDMI_EDID_REQ_REG, 0);
  564. data = 0;
  565. for (attempt = 0; attempt < MAX_READ_ATTEMPTS; attempt++)
  566. {
  567. tda19988_hdmi_getregs(priv, HDMI_CTRL_INT_REG, &data, 1);
  568. if ((data & HDMI_CTRL_INT_EDID) != 0)
  569. {
  570. break;
  571. }
  572. }
  573. if (attempt == MAX_READ_ATTEMPTS)
  574. {
  575. ret = -ETIMEDOUT;
  576. goto done;
  577. }
  578. if (tda19988_hdmi_getregs(priv, HDMI_EDID_DATA_REG, buf, EDID_LENGTH) != 0)
  579. {
  580. ret = -EIO;
  581. goto done;
  582. }
  583. done:
  584. tda19988_hdmi_modifyreg(priv, HDMI_CTRL_INT_REG, HDMI_CTRL_INT_EDID, 0);
  585. return ret;
  586. }
  587. /****************************************************************************
  588. * Name: tda19988_fetch_edid
  589. *
  590. * Description:
  591. * Fetch the EDID block from the DSD.
  592. *
  593. * Returned Value:
  594. * Zero (OK) is returned on success; A negated errno value is returned on
  595. * any failure.
  596. *
  597. ****************************************************************************/
  598. static int tda19988_fetch_edid(struct tda1988_dev_s *priv)
  599. {
  600. int blocks;
  601. int ret;
  602. ret = 0;
  603. if (priv->version == HDMI_CTRL_REV_TDA19988)
  604. {
  605. tda19988_hdmi_modifyreg(priv, HDMI_HDCPOTP_TX4_REG,
  606. HDMI_HDCPOTP_TX4_PDRAM, 0);
  607. }
  608. ret = tda19988_fetch_edid_block(priv, priv->edid, 0);
  609. if (ret < 0)
  610. {
  611. goto done;
  612. }
  613. blocks = priv->edid[EDID_TRAILER_NEXTENSIONS_OFFSET];
  614. if (blocks > 0)
  615. {
  616. FAR uint8_t *edid;
  617. unsigned int edid_len;
  618. int i;
  619. edid_len = EDID_LENGTH * (blocks + 1);
  620. edid = (FAR void *)kmm_realloc(priv->edid, edid_len);
  621. if (edid == NULL)
  622. {
  623. lcderr("ERROR: Failed to kmm_realloc EDID\n");
  624. ret = -ENOMEM;
  625. goto done;
  626. }
  627. priv->edid = edid;
  628. priv->edid_len = edid_len;
  629. for (i = 0; i < blocks; i++)
  630. {
  631. FAR uint8_t *buf;
  632. /* TODO: check validity */
  633. buf = priv->edid + EDID_LENGTH * (i + 1);
  634. ret = tda19988_fetch_edid_block(priv, buf, i);
  635. if (ret < 0)
  636. {
  637. lcderr("ERROR: tda19988_fetch_edid_block failed: %d\n", ret);
  638. goto done;
  639. }
  640. }
  641. }
  642. done:
  643. if (priv->version == HDMI_CTRL_REV_TDA19988)
  644. {
  645. tda19988_hdmi_modifyreg(priv, HDMI_HDCPOTP_TX4_REG,
  646. 0, HDMI_HDCPOTP_TX4_PDRAM);
  647. }
  648. return ret;
  649. }
  650. /****************************************************************************
  651. * Name: tda19988_read_internal
  652. *
  653. * Description:
  654. * Return the previously read EDID data.
  655. *
  656. * Returned Value:
  657. * The number of bytes actually read is returned on success; A negated
  658. * errno value is returned on any failure.
  659. *
  660. ****************************************************************************/
  661. static ssize_t tda19988_read_internal(FAR struct tda1988_dev_s *priv,
  662. off_t offset, FAR uint8_t *buffer,
  663. size_t buflen)
  664. {
  665. DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
  666. /* Check if the offset lies outside of the EDID buffer */
  667. DEBUGASSERT(priv->edid != NULL && priv->edid_len > 0);
  668. if (offset < 0)
  669. {
  670. offset = 0;
  671. }
  672. else if (offset >= priv->edid_len)
  673. {
  674. return 0; /* End-of-file */
  675. }
  676. /* Clip the number of bytes so that the read region is wholly
  677. * within the EDID buffer.
  678. */
  679. if (offset + buflen > priv->edid_len)
  680. {
  681. buflen = priv->edid_len - offset;
  682. }
  683. memcpy(buffer, &priv->edid[offset], buflen);
  684. return (ssize_t)buflen;
  685. }
  686. /****************************************************************************
  687. * Name: tda19988_open
  688. *
  689. * Description:
  690. * Standard character driver open method.
  691. *
  692. * Returned Value:
  693. * Zero (OK) is returned on success; A negated errno value is returned on
  694. * any failure.
  695. *
  696. ****************************************************************************/
  697. static int tda19988_open(FAR struct file *filep)
  698. {
  699. FAR struct inode *inode;
  700. FAR struct tda1988_dev_s *priv;
  701. int ret;
  702. /* Get the private driver state instance */
  703. DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
  704. inode = filep->f_inode;
  705. priv = (FAR struct tda1988_dev_s *)inode->i_private;
  706. DEBUGASSERT(priv != NULL);
  707. /* Get exclusive access to the driver instance */
  708. ret = nxsem_wait(&priv->exclsem);
  709. if (ret < 0)
  710. {
  711. return ret;
  712. }
  713. /* Increment the reference count on the driver instance */
  714. DEBUGASSERT(priv->crefs != UINT8_MAX);
  715. priv->crefs++;
  716. nxsem_post(&priv->exclsem);
  717. return OK;
  718. }
  719. /****************************************************************************
  720. * Name: tda19988_close
  721. *
  722. * Description:
  723. * Standard character driver cl;ose method.
  724. *
  725. * Returned Value:
  726. * Zero (OK) is returned on success; A negated errno value is returned on
  727. * any failure.
  728. *
  729. ****************************************************************************/
  730. static int tda19988_close(FAR struct file *filep)
  731. {
  732. FAR struct inode *inode;
  733. FAR struct tda1988_dev_s *priv;
  734. int ret;
  735. /* Get the private driver state instance */
  736. DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
  737. inode = filep->f_inode;
  738. priv = (FAR struct tda1988_dev_s *)inode->i_private;
  739. DEBUGASSERT(priv != NULL);
  740. /* Get exclusive access to the driver */
  741. ret = nxsem_wait(&priv->exclsem);
  742. if (ret < 0)
  743. {
  744. return ret;
  745. }
  746. /* Decrement the count of open references on the driver */
  747. DEBUGASSERT(priv->crefs > 0);
  748. priv->crefs--;
  749. #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
  750. /* If the count has decremented to zero and the driver has been unlinked,
  751. * then self-destruct now.
  752. */
  753. if (priv->crefs == 0 && priv->unlinked)
  754. {
  755. tda19988_shutdown(priv);
  756. return OK;
  757. }
  758. #endif
  759. nxsem_post(&priv->exclsem);
  760. return -ENOSYS;
  761. }
  762. /****************************************************************************
  763. * Name: tda19988_read
  764. *
  765. * Description:
  766. * Standard character driver read method.
  767. *
  768. * Returned Value:
  769. * The number of bytes read is returned on success; A negated errno value
  770. * is returned on any failure. End-of-file (zero) is never returned.
  771. *
  772. ****************************************************************************/
  773. static ssize_t tda19988_read(FAR struct file *filep, FAR char *buffer,
  774. size_t len)
  775. {
  776. FAR struct inode *inode;
  777. FAR struct tda1988_dev_s *priv;
  778. ssize_t nread;
  779. int ret;
  780. /* Get the private driver state instance */
  781. DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
  782. inode = filep->f_inode;
  783. priv = (FAR struct tda1988_dev_s *)inode->i_private;
  784. DEBUGASSERT(priv != NULL);
  785. /* Get exclusive access to the driver */
  786. ret = nxsem_wait(&priv->exclsem);
  787. if (ret < 0)
  788. {
  789. return ret;
  790. }
  791. /* Return the previously read EDID data */
  792. nread = tda19988_read_internal(priv, filep->f_pos, (FAR uint8_t *)buffer,
  793. len);
  794. if (nread > 0)
  795. {
  796. filep->f_pos += nread;
  797. }
  798. nxsem_post(&priv->exclsem);
  799. return nread;
  800. }
  801. /****************************************************************************
  802. * Name: tda19988_write
  803. *
  804. * Description:
  805. * Standard character driver write method.
  806. *
  807. * Returned Value:
  808. * The number of bytes written is returned on success; A negated errno
  809. * value is returned on any failure.
  810. *
  811. ****************************************************************************/
  812. static ssize_t tda19988_write(FAR struct file *filep, FAR const char *buffer,
  813. size_t len)
  814. {
  815. /* Driver may be opened for write access. Writing, however, is not
  816. * supported.
  817. */
  818. return -ENOSYS;
  819. }
  820. /****************************************************************************
  821. * Name: tda19988_seek
  822. *
  823. * Description:
  824. * Standard character driver poll method.
  825. *
  826. * Returned Value:
  827. * The current file position is returned on success; A negated errno value
  828. * is returned on any failure.
  829. *
  830. ****************************************************************************/
  831. static off_t tda19988_seek(FAR struct file *filep, off_t offset, int whence)
  832. {
  833. FAR struct inode *inode;
  834. FAR struct tda1988_dev_s *priv;
  835. off_t pos;
  836. int ret;
  837. /* Get the private driver state instance */
  838. DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
  839. inode = filep->f_inode;
  840. priv = (FAR struct tda1988_dev_s *)inode->i_private;
  841. DEBUGASSERT(priv != NULL);
  842. /* Get exclusive access to the driver */
  843. ret = nxsem_wait(&priv->exclsem);
  844. if (ret < 0)
  845. {
  846. return ret;
  847. }
  848. /* Perform the seek operation */
  849. pos = filep->f_pos;
  850. switch (whence)
  851. {
  852. case SEEK_CUR:
  853. pos += offset;
  854. if (pos > EDID_LENGTH)
  855. {
  856. pos = EDID_LENGTH;
  857. }
  858. else if (pos < 0)
  859. {
  860. pos = 0;
  861. }
  862. filep->f_pos = pos;
  863. break;
  864. case SEEK_SET:
  865. pos = offset;
  866. if (pos > EDID_LENGTH)
  867. {
  868. pos = EDID_LENGTH;
  869. }
  870. else if (pos < 0)
  871. {
  872. pos = 0;
  873. }
  874. filep->f_pos = pos;
  875. break;
  876. case SEEK_END:
  877. pos = EDID_LENGTH + offset;
  878. if (pos > EDID_LENGTH)
  879. {
  880. pos = EDID_LENGTH;
  881. }
  882. else if (pos < 0)
  883. {
  884. pos = 0;
  885. }
  886. filep->f_pos = pos;
  887. break;
  888. default:
  889. /* Return EINVAL if the whence argument is invalid */
  890. pos = (off_t)-EINVAL;
  891. break;
  892. }
  893. nxsem_post(&priv->exclsem);
  894. return pos;
  895. }
  896. /****************************************************************************
  897. * Name: tda19988_ioctl
  898. *
  899. * Description:
  900. * Standard character driver poll method.
  901. *
  902. * Returned Value:
  903. * Zero (OK) is returned on success; A negated errno value is returned on
  904. * any failure.
  905. *
  906. ****************************************************************************/
  907. static int tda19988_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
  908. {
  909. FAR struct inode *inode;
  910. FAR struct tda1988_dev_s *priv;
  911. int ret;
  912. /* Get the private driver state instance */
  913. DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
  914. inode = filep->f_inode;
  915. priv = (FAR struct tda1988_dev_s *)inode->i_private;
  916. DEBUGASSERT(priv != NULL);
  917. /* Get exclusive access to the driver */
  918. ret = nxsem_wait(&priv->exclsem);
  919. if (ret < 0)
  920. {
  921. return ret;
  922. }
  923. /* Handle the IOCTL command */
  924. switch (cmd)
  925. {
  926. /* TDA19988_IOC_VIDEOMODE:
  927. * Description: Select the video mode. This must be done as part
  928. * of the initialization of the driver. This is
  929. * equivalent to calling tda18899_videomode() within
  930. * the OS.
  931. * Argument: A reference to a videomode_s structure
  932. * instance.
  933. * Returns: None
  934. */
  935. case TDA19988_IOC_VIDEOMODE:
  936. {
  937. FAR const struct videomode_s *mode =
  938. (FAR const struct videomode_s *)((uintptr_t)arg);
  939. if (mode == NULL)
  940. {
  941. ret = -EINVAL;
  942. }
  943. else
  944. {
  945. ret = tda19988_videomode_internal(priv, mode);
  946. if (ret < 0)
  947. {
  948. lcderr("ERROR: tda19988_videomode_internal failed: %d\n",
  949. ret);
  950. }
  951. }
  952. }
  953. break;
  954. default:
  955. ret = -ENOTTY;
  956. break;
  957. }
  958. nxsem_post(&priv->exclsem);
  959. return ret;
  960. }
  961. /****************************************************************************
  962. * Name: tda19988_poll
  963. *
  964. * Description:
  965. * Standard character driver poll method.
  966. *
  967. * Returned Value:
  968. * Zero (OK) is returned on success; A negated errno value is returned on
  969. * any failure.
  970. *
  971. ****************************************************************************/
  972. static int tda19988_poll(FAR struct file *filep, FAR struct pollfd *fds,
  973. bool setup)
  974. {
  975. FAR struct inode *inode;
  976. FAR struct tda1988_dev_s *priv;
  977. int ret;
  978. /* Get the private driver state instance */
  979. DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
  980. inode = filep->f_inode;
  981. priv = (FAR struct tda1988_dev_s *)inode->i_private;
  982. DEBUGASSERT(priv != NULL);
  983. /* Get exclusive access to the driver */
  984. ret = nxsem_wait(&priv->exclsem);
  985. if (ret < 0)
  986. {
  987. return ret;
  988. }
  989. if (setup)
  990. {
  991. fds->revents |= (fds->events & (POLLIN | POLLOUT));
  992. if (fds->revents != 0)
  993. {
  994. nxsem_post(fds->sem);
  995. }
  996. }
  997. nxsem_post(&priv->exclsem);
  998. return OK;
  999. }
  1000. /****************************************************************************
  1001. * Name: tda19988_unlink
  1002. *
  1003. * Description:
  1004. * Standard character driver unlink method.
  1005. *
  1006. * Returned Value:
  1007. * Zero (OK) is returned on success; A negated errno value is returned on
  1008. * any failure.
  1009. *
  1010. ****************************************************************************/
  1011. #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
  1012. static int tda19988_unlink(FAR struct inode *inode)
  1013. {
  1014. FAR struct tda1988_dev_s *priv;
  1015. int ret;
  1016. /* Get the private driver state instance */
  1017. DEBUGASSERT(inode != NULL && inode->i_private != NULL);
  1018. priv = (FAR struct tda1988_dev_s *)inode->i_private;
  1019. /* Get exclusive access to the driver */
  1020. ret = nxsem_wait(&priv->exclsem);
  1021. if (ret < 0)
  1022. {
  1023. return ret;
  1024. }
  1025. /* Are there open references to the driver data structure? */
  1026. if (priv->crefs <= 0)
  1027. {
  1028. tda19988_shutdown(priv);
  1029. return OK;
  1030. }
  1031. /* No... just mark the driver as unlinked and free the resources when the
  1032. * last client closes their reference to the driver.
  1033. */
  1034. priv->unlinked = true;
  1035. nxsem_post(&priv->exclsem);
  1036. return OK;
  1037. }
  1038. #endif
  1039. /****************************************************************************
  1040. * Name: tda19988_hwinitialize
  1041. *
  1042. * Description:
  1043. * Initialize the TDA19988 hardware.
  1044. *
  1045. * Input Parameters:
  1046. * priv - TDA19988 driver state
  1047. *
  1048. * Returned Value:
  1049. * Zero (OK) is returned on success; a negated errno value is returned on
  1050. * any failure.
  1051. *
  1052. ****************************************************************************/
  1053. static int tda19988_hwinitialize(FAR struct tda1988_dev_s *priv)
  1054. {
  1055. uint16_t version;
  1056. uint8_t data;
  1057. int ret;
  1058. tda19988_cec_putreg(priv, CEC_ENAMODS_REG,
  1059. CEC_ENAMODS_RXSENS | CEC_ENAMODS_HDMI);
  1060. up_udelay(1000);
  1061. tda19988_cec_getregs(priv, CEC_STATUS_REG, &data, 1);
  1062. /* Reset core */
  1063. tda19988_hdmi_modifyreg(priv, HDMI_CTRL_RESET_REG, 0, 3);
  1064. up_udelay(100);
  1065. tda19988_hdmi_modifyreg(priv, HDMI_CTRL_RESET_REG, 3, 0);
  1066. up_udelay(100);
  1067. /* Reset transmitter: */
  1068. tda19988_hdmi_modifyreg(priv, HDMI_CTRL_MAIN_CNTRL0_REG, 0,
  1069. HDMI_CTRL_MAIN_CNTRL0_SR);
  1070. tda19988_hdmi_modifyreg(priv, HDMI_CTRL_MAIN_CNTRL0_REG,
  1071. HDMI_CTRL_MAIN_CNTRL0_SR, 0);
  1072. /* PLL registers common configuration */
  1073. tda19988_hdmi_putreg(priv, HDMI_PLL_SERIAL_1_REG, 0x00);
  1074. tda19988_hdmi_putreg(priv, HDMI_PLL_SERIAL_2_REG,
  1075. HDMI_PLL_SERIAL_2_SRL_NOSC(1));
  1076. tda19988_hdmi_putreg(priv, HDMI_PLL_SERIAL_3_REG, 0x00);
  1077. tda19988_hdmi_putreg(priv, HDMI_PLL_SERIALIZER_REG, 0x00);
  1078. tda19988_hdmi_putreg(priv, HDMI_PLL_BUFFER_OUT_REG, 0x00);
  1079. tda19988_hdmi_putreg(priv, HDMI_PLL_SCG1_REG, 0x00);
  1080. tda19988_hdmi_putreg(priv, HDMI_PLL_SEL_CLK_REG,
  1081. HDMI_PLL_SEL_CLK_SEL_CLK1 |
  1082. HDMI_PLL_SEL_CLK_ENA_SC_CLK);
  1083. tda19988_hdmi_putreg(priv, HDMI_PLL_SCGN1_REG, 0xfa);
  1084. tda19988_hdmi_putreg(priv, HDMI_PLL_SCGN2_REG, 0x00);
  1085. tda19988_hdmi_putreg(priv, HDMI_PLL_SCGR1_REG, 0x5b);
  1086. tda19988_hdmi_putreg(priv, HDMI_PLL_SCGR2_REG, 0x00);
  1087. tda19988_hdmi_putreg(priv, HDMI_PLL_SCG2_REG, 0x10);
  1088. /* Write the default value MUX register */
  1089. tda19988_hdmi_putreg(priv, HDMI_CTRL_MUX_VP_VIP_OUT_REG, 0x24);
  1090. tda19988_hdmi_getregs(priv, HDMI_CTRL_REV_LO_REG, &data, 1);
  1091. version = (uint16_t)data;
  1092. tda19988_hdmi_getregs(priv, HDMI_CTRL_REV_HI_REG, &data, 1);
  1093. version |= ((uint16_t)data << 8);
  1094. /* Clear feature bits */
  1095. priv->version = version & ~0x30;
  1096. switch (priv->version)
  1097. {
  1098. case HDMI_CTRL_REV_TDA19988:
  1099. lcdinfo("TDA19988\n");
  1100. break;
  1101. default:
  1102. lcderr("ERROR: Unknown device: %04x\n", priv->version);
  1103. ret = -ENODEV;
  1104. goto done;
  1105. }
  1106. tda19988_hdmi_putreg(priv, HDMI_CTRL_DDC_CTRL_REG, HDMI_CTRL_DDC_EN);
  1107. tda19988_hdmi_putreg(priv, HDMI_HDCPOTP_TX3_REG, 39);
  1108. tda19988_cec_putreg(priv, CEC_FRO_IM_CLK_CTRL_REG,
  1109. CEC_FRO_IM_CLK_CTRL_GHOST_DIS |
  1110. CEC_FRO_IM_CLK_CTRL_IMCLK_SEL);
  1111. ret = tda19988_fetch_edid(priv);
  1112. if (ret < 0)
  1113. {
  1114. lcderr("ERROR: tda19988_fetch_edid failed: %d\n", ret);
  1115. goto done;
  1116. }
  1117. /* Default values for RGB 4:4:4 mapping */
  1118. tda19988_hdmi_putreg(priv, HDMI_CTRL_VIPCTRL_0_REG, 0x23);
  1119. tda19988_hdmi_putreg(priv, HDMI_CTRL_VIPCTRL_1_REG, 0x01);
  1120. tda19988_hdmi_putreg(priv, HDMI_CTRL_VIPCTRL_2_REG, 0x45);
  1121. ret = OK;
  1122. done:
  1123. return ret;
  1124. }
  1125. /****************************************************************************
  1126. * Name: tda19988_videomode_internal
  1127. *
  1128. * Description:
  1129. * Initialize the TDA19988 driver to a specified video mode. This is a
  1130. * necessary part of the TDA19988 initialization: A video mode must be
  1131. * configured before the driver is usable.
  1132. *
  1133. * Input Parameters:
  1134. * priv - TDA19988 driver state
  1135. * mode - The new video mode.
  1136. *
  1137. * Returned Value:
  1138. * Zero (OK) is returned on success; a negated errno value is returned on
  1139. * any failure.
  1140. *
  1141. ****************************************************************************/
  1142. static int
  1143. tda19988_videomode_internal(FAR struct tda1988_dev_s *priv,
  1144. FAR const struct videomode_s *mode)
  1145. {
  1146. uint16_t ref_pix;
  1147. uint16_t ref_line;
  1148. uint16_t n_pix;
  1149. uint16_t n_line;
  1150. uint16_t hs_pix_start;
  1151. uint16_t hs_pix_stop;
  1152. uint16_t vs1_pix_start;
  1153. uint16_t vs1_pix_stop;
  1154. uint16_t vs1_line_start;
  1155. uint16_t vs1_line_end;
  1156. uint16_t vs2_pix_start;
  1157. uint16_t vs2_pix_stop;
  1158. uint16_t vs2_line_start;
  1159. uint16_t vs2_line_end;
  1160. uint16_t vwin1_line_start;
  1161. uint16_t vwin1_line_end;
  1162. uint16_t vwin2_line_start;
  1163. uint16_t vwin2_line_end;
  1164. uint16_t de_start;
  1165. uint16_t de_stop;
  1166. uint8_t regval;
  1167. uint8_t div;
  1168. DEBUGASSERT(priv != NULL && mode != NULL);
  1169. n_pix = mode->htotal;
  1170. n_line = mode->vtotal;
  1171. hs_pix_stop = mode->hsync_end - mode->hdisplay;
  1172. hs_pix_start = mode->hsync_start - mode->hdisplay;
  1173. de_stop = mode->htotal;
  1174. de_start = mode->htotal - mode->hdisplay;
  1175. ref_pix = hs_pix_start + 3;
  1176. if (mode->flags & VID_HSKEW)
  1177. {
  1178. ref_pix += mode->hskew;
  1179. }
  1180. if ((mode->flags & VID_INTERLACE) == 0)
  1181. {
  1182. ref_line = 1 + mode->vsync_start - mode->vdisplay;
  1183. vwin1_line_start = mode->vtotal - mode->vdisplay - 1;
  1184. vwin1_line_end = vwin1_line_start + mode->vdisplay;
  1185. vs1_pix_start = vs1_pix_stop = hs_pix_start;
  1186. vs1_line_start = mode->vsync_start - mode->vdisplay;
  1187. vs1_line_end = vs1_line_start + mode->vsync_end -
  1188. mode->vsync_start;
  1189. vwin2_line_start = vwin2_line_end = 0;
  1190. vs2_pix_start = vs2_pix_stop = 0;
  1191. vs2_line_start = vs2_line_end = 0;
  1192. }
  1193. else
  1194. {
  1195. ref_line = 1 + (mode->vsync_start - mode->vdisplay) / 2;
  1196. vwin1_line_start = (mode->vtotal - mode->vdisplay) / 2;
  1197. vwin1_line_end = vwin1_line_start + mode->vdisplay / 2;
  1198. vs1_pix_start = vs1_pix_stop = hs_pix_start;
  1199. vs1_line_start = (mode->vsync_start - mode->vdisplay) / 2;
  1200. vs1_line_end = vs1_line_start +
  1201. (mode->vsync_end - mode->vsync_start) / 2;
  1202. vwin2_line_start = vwin1_line_start + mode->vtotal / 2;
  1203. vwin2_line_end = vwin2_line_start + mode->vdisplay / 2;
  1204. vs2_pix_start = vs2_pix_stop = hs_pix_start + mode->htotal / 2;
  1205. vs2_line_start = vs1_line_start + mode->vtotal / 2;
  1206. vs2_line_end = vs2_line_start +
  1207. (mode->vsync_end - mode->vsync_start) / 2;
  1208. }
  1209. div = 148500 / mode->dotclock;
  1210. if (div != 0)
  1211. {
  1212. if (--div > 3)
  1213. {
  1214. div = 3;
  1215. }
  1216. }
  1217. /* Set HDMI HDCP mode off */
  1218. tda19988_hdmi_modifyreg(priv, HDMI_CTRL_TBG_CNTRL_1_REG, 0,
  1219. HDMI_CTRL_TBG_CNTRL_1_DWIN_DIS);
  1220. tda19988_hdmi_modifyreg(priv, HDMI_HDCPOTP_TX33_REG,
  1221. HDMI_HDCPOTP_TX33_HDMI, 0);
  1222. tda19988_hdmi_putreg(priv, HDMI_AUDIO_ENC_CTRL_REG,
  1223. HDMI_AUDIO_ENC_CNTRL_DVI_MODE);
  1224. /* No pre-filter or interpreter */
  1225. tda19988_hdmi_putreg(priv, HDMI_CTRL_HVF_CNTRL_0_REG,
  1226. HDMI_CTRL_HVF_CNTRL_0_INTPOL_BYPASS |
  1227. HDMI_CTRL_HVF_CNTRL_0_PREFIL_NONE);
  1228. tda19988_hdmi_putreg(priv, HDMI_CTRL_VIPCTRL_5_REG,
  1229. HDMI_CTRL_VIPCTRL_5_SP_CNT(0));
  1230. tda19988_hdmi_putreg(priv, HDMI_CTRL_VIPCTRL_4_REG,
  1231. HDMI_CTRL_VIPCTRL_4_BLANKIT_NDE |
  1232. HDMI_CTRL_VIPCTRL_4_BLC_NONE);
  1233. tda19988_hdmi_modifyreg(priv, HDMI_PLL_SERIAL_3_REG,
  1234. HDMI_PLL_SERIAL_3_SRL_CCIR, 0);
  1235. tda19988_hdmi_modifyreg(priv, HDMI_PLL_SERIAL_1_REG,
  1236. HDMI_PLL_SERIAL_1_SRL_MAN_IP, 0);
  1237. tda19988_hdmi_modifyreg(priv, HDMI_PLL_SERIAL_3_REG,
  1238. HDMI_PLL_SERIAL_3_SRL_DE, 0);
  1239. tda19988_hdmi_putreg(priv, HDMI_PLL_SERIALIZER_REG, 0);
  1240. tda19988_hdmi_putreg(priv, HDMI_CTRL_HVF_CNTRL_1_REG,
  1241. HDMI_CTRL_HVF_CNTRL_1_VQR_FULL);
  1242. tda19988_hdmi_putreg(priv, HDMI_CTRL_RPT_CNTRL_REG, 0);
  1243. tda19988_hdmi_putreg(priv, HDMI_PLL_SEL_CLK_REG,
  1244. HDMI_PLL_SEL_CLK_SEL_VRF_CLK(0) |
  1245. HDMI_PLL_SEL_CLK_SEL_CLK1 |
  1246. HDMI_PLL_SEL_CLK_ENA_SC_CLK);
  1247. tda19988_hdmi_putreg(priv, HDMI_PLL_SERIAL_2_REG,
  1248. HDMI_PLL_SERIAL_2_SRL_NOSC(div) |
  1249. HDMI_PLL_SERIAL_2_SRL_PR(0));
  1250. tda19988_hdmi_modifyreg(priv, HDMI_CTRL_MATCTRL_REG, 0,
  1251. HDMI_CTRL_MAT_CONTRL_MAT_BP);
  1252. tda19988_hdmi_putreg(priv, HDMI_PLL_ANA_GENERAL_REG, 0x09);
  1253. tda19988_hdmi_modifyreg(priv, HDMI_CTRL_TBG_CNTRL_0_REG,
  1254. HDMI_CTRL_TBG_CNTRL_0_SYNC_MTHD, 0);
  1255. /* Sync on rising HSYNC/VSYNC */
  1256. regval = HDMI_CTRL_VIPCTRL_3_SYNC_HS;
  1257. if (mode->flags & VID_NHSYNC)
  1258. {
  1259. regval |= HDMI_CTRL_VIPCTRL_3_H_TGL;
  1260. }
  1261. if (mode->flags & VID_NVSYNC)
  1262. {
  1263. regval |= HDMI_CTRL_VIPCTRL_3_V_TGL;
  1264. }
  1265. tda19988_hdmi_putreg(priv, HDMI_CTRL_VIPCTRL_3_REG, regval);
  1266. regval = HDMI_CTRL_TBG_CNTRL_1_TGL_EN;
  1267. if (mode->flags & VID_NHSYNC)
  1268. {
  1269. regval |= HDMI_CTRL_TBG_CNTRL_1_H_TGL;
  1270. }
  1271. if (mode->flags & VID_NVSYNC)
  1272. {
  1273. regval |= HDMI_CTRL_TBG_CNTRL_1_V_TGL;
  1274. }
  1275. tda19988_hdmi_putreg(priv, HDMI_CTRL_TBG_CNTRL_1_REG, regval);
  1276. /* Program timing */
  1277. tda19988_hdmi_putreg(priv, HDMI_CTRL_MUX_VIDFORMAT_REG, 0x00);
  1278. tda19988_hdmi_putreg16(priv, HDMI_CTRL_MUX_REFPIX_MSB_REG, ref_pix);
  1279. tda19988_hdmi_putreg16(priv, HDMI_CTRL_MUX_REFLINE_MSB_REG, ref_line);
  1280. tda19988_hdmi_putreg16(priv, HDMI_CTRL_MUX_NPIX_MSB_REG, n_pix);
  1281. tda19988_hdmi_putreg16(priv, HDMI_CTRL_MUX_NLINE_MSB_REG, n_line);
  1282. tda19988_hdmi_putreg16(priv, HDMI_CTRL_MUX_VS_LINE_STRT_1_MSB_REG,
  1283. vs1_line_start);
  1284. tda19988_hdmi_putreg16(priv, HDMI_CTRL_MUX_VS_PIX_STRT_1_MSB_REG,
  1285. vs1_pix_start);
  1286. tda19988_hdmi_putreg16(priv, HDMI_CTRL_VS_LINE_END_1_MSB_REG,
  1287. vs1_line_end);
  1288. tda19988_hdmi_putreg16(priv, HDMI_CTRL_VS_PIX_END_1_MSB_REG, vs1_pix_stop);
  1289. tda19988_hdmi_putreg16(priv, HDMI_CTRL_VS_LINE_STRT_2_MSB_REG,
  1290. vs2_line_start);
  1291. tda19988_hdmi_putreg16(priv, HDMI_CTRL_VS_PIX_STRT_2_MSB_REG,
  1292. vs2_pix_start);
  1293. tda19988_hdmi_putreg16(priv, HDMI_CTRL_VS_LINE_END_2_MSB_REG,
  1294. vs2_line_end);
  1295. tda19988_hdmi_putreg16(priv, HDMI_CTRL_VS_PIX_END_2_MSB_REG, vs2_pix_stop);
  1296. tda19988_hdmi_putreg16(priv, HDMI_CTRL_HS_PIX_START_MSB_REG, hs_pix_start);
  1297. tda19988_hdmi_putreg16(priv, HDMI_CTRL_HS_PIX_STOP_MSB_REG, hs_pix_stop);
  1298. tda19988_hdmi_putreg16(priv, HDMI_CTRL_VWIN_START_1_MSB_REG,
  1299. vwin1_line_start);
  1300. tda19988_hdmi_putreg16(priv, HDMI_CTRL_VWIN_END_1_MSB_REG, vwin1_line_end);
  1301. tda19988_hdmi_putreg16(priv, HDMI_CTRL_VWIN_START_2_MSB_REG,
  1302. vwin2_line_start);
  1303. tda19988_hdmi_putreg16(priv, HDMI_CTRL_VWIN_END_2_MSB_REG, vwin2_line_end);
  1304. tda19988_hdmi_putreg16(priv, HDMI_CTRL_DE_START_MSB_REG, de_start);
  1305. tda19988_hdmi_putreg16(priv, HDMI_CTRL_DE_STOP_MSB_REG, de_stop);
  1306. if (priv->version == HDMI_CTRL_REV_TDA19988)
  1307. {
  1308. tda19988_hdmi_putreg(priv, HDMI_CTRL_ENABLE_SPACE_REG, 0x00);
  1309. }
  1310. /* Must be last register set */
  1311. tda19988_hdmi_modifyreg(priv, HDMI_CTRL_TBG_CNTRL_0_REG,
  1312. HDMI_CTRL_TBG_CNTRL_0_SYNC_ONCE, 0);
  1313. return OK;
  1314. }
  1315. /****************************************************************************
  1316. * Name: tda19988_shutdown
  1317. *
  1318. * Description:
  1319. * Free resources used by the driver when it has been unlinked.
  1320. *
  1321. * Returned Value:
  1322. * None.
  1323. *
  1324. ****************************************************************************/
  1325. #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
  1326. static void tda19988_shutdown(FAR struct tda1988_dev_s *priv)
  1327. {
  1328. /* Detach and disable interrupts */
  1329. if (priv->lower != NULL) /* May be called before fully initialized */
  1330. {
  1331. DEBUGASSERT(priv->lower->attach != NULL &&
  1332. priv->lower->enable != NULL);
  1333. priv->lower->attach(priv->lower, NULL, NULL);
  1334. priv->lower->enable(priv->lower, false);
  1335. }
  1336. /* Release resources */
  1337. nxsem_destroy(&priv->exclsem);
  1338. /* Free memory */
  1339. if (priv->edid != NULL)
  1340. {
  1341. kmm_free(priv->edid);
  1342. }
  1343. kmm_free(priv);
  1344. }
  1345. #endif
  1346. /****************************************************************************
  1347. * Public Functions
  1348. ****************************************************************************/
  1349. /****************************************************************************
  1350. * Name: tda19988_register
  1351. *
  1352. * Description:
  1353. * Create and register the the TDA19988 driver at 'devpath'
  1354. *
  1355. * Input Parameters:
  1356. * devpath - The location to register the TDA19988 driver instance. The
  1357. * standard location would be a path like /dev/hdmi0.
  1358. * lower - The interface to the the TDA19988 lower half driver.
  1359. *
  1360. * Returned Value:
  1361. * On success, non-NULL handle is returned that may be subsequently used
  1362. * with tda19988_videomode(). NULL is returned on failure.
  1363. *
  1364. ****************************************************************************/
  1365. TDA19988_HANDLE tda19988_register(FAR const char *devpath,
  1366. FAR const struct tda19988_lower_s *lower)
  1367. {
  1368. FAR struct tda1988_dev_s *priv;
  1369. int ret;
  1370. DEBUGASSERT(devpath != NULL && lower != NULL);
  1371. /* Allocate an instance of the TDA19988 driver */
  1372. priv = (FAR struct tda1988_dev_s *)
  1373. kmm_zalloc(sizeof(struct tda1988_dev_s));
  1374. if (priv == NULL)
  1375. {
  1376. lcderr("ERROR: Failed to allocate device structure\n");
  1377. return NULL;
  1378. }
  1379. /* Assume a single block in EDID */
  1380. priv->edid = (FAR uint8_t *)kmm_malloc(EDID_LENGTH);
  1381. if (priv->edid == NULL)
  1382. {
  1383. lcderr("ERROR: Failed to allocate EDID\n");
  1384. tda19988_shutdown(priv);
  1385. return NULL;
  1386. }
  1387. priv->edid_len = EDID_LENGTH;
  1388. /* Initialize the driver structure */
  1389. priv->lower = lower;
  1390. priv->page = HDMI_NO_PAGE;
  1391. nxsem_init(&priv->exclsem, 0, 1);
  1392. /* Initialize the TDA19988 */
  1393. ret = tda19988_hwinitialize(priv);
  1394. if (ret < 0)
  1395. {
  1396. lcderr("ERROR: tda19988_hwinitialize failed: %d\n", ret);
  1397. tda19988_shutdown(priv);
  1398. return NULL;
  1399. }
  1400. /* Register the driver */
  1401. ret = register_driver(devpath, &tda19988_fops, 0666, NULL);
  1402. if (ret < 0)
  1403. {
  1404. lcderr("ERROR: register_driver() failed: %d\n", ret);
  1405. tda19988_shutdown(priv);
  1406. return NULL;
  1407. }
  1408. return (TDA19988_HANDLE)priv;
  1409. }
  1410. /****************************************************************************
  1411. * Name: tda19988_videomode
  1412. *
  1413. * Description:
  1414. * Initialize the TDA19988 driver to a specified video mode. This is a
  1415. * necessary part of the TDA19988 initialization: A video mode must be
  1416. * configured before the driver is usable.
  1417. *
  1418. * NOTE: This may be done in two ways: (1) via a call to
  1419. * tda19988_videomode() from board-specific logic within the OS, or
  1420. * equivalently (2) using the TDA19988_IOC_VIDEOMODE from application
  1421. * logic outside of the OS.
  1422. *
  1423. * Input Parameters:
  1424. * handle - The handle previously returned by tda19988_register().
  1425. * mode - The new video mode.
  1426. *
  1427. * Returned Value:
  1428. * Zero (OK) is returned on success; a negated errno value is returned on
  1429. * any failure.
  1430. *
  1431. ****************************************************************************/
  1432. int tda19988_videomode(TDA19988_HANDLE handle,
  1433. FAR const struct videomode_s *mode)
  1434. {
  1435. FAR struct tda1988_dev_s *priv = (FAR struct tda1988_dev_s *)handle;
  1436. int ret;
  1437. DEBUGASSERT(priv != NULL && mode != NULL);
  1438. /* Get exclusive access to the driver */
  1439. ret = nxsem_wait(&priv->exclsem);
  1440. if (ret < 0)
  1441. {
  1442. return ret;
  1443. }
  1444. /* Defer the heavy lifting to tda19988_videomode_internal() */
  1445. ret = tda19988_videomode_internal(priv, mode);
  1446. if (ret < 0)
  1447. {
  1448. lcderr("ERROR: tda19988_videomode_internal failed: %d\n", ret);
  1449. }
  1450. nxsem_post(&priv->exclsem);
  1451. return ret;
  1452. }
  1453. /****************************************************************************
  1454. * Name: tda19988_read_edid
  1455. *
  1456. * Description:
  1457. * Read the EDID (Extended Display Identification Data).
  1458. *
  1459. * NOTE: This may be done in two ways: (1) via a call to
  1460. * tda19988_read_edid() from board-specific logic within the OS, or
  1461. * equivalently (2) using a standard read() to read the EDID from
  1462. * application logic outside of the OS.
  1463. *
  1464. * Input Parameters:
  1465. * handle - The handle previously returned by tda19988_register().
  1466. * offset - The offset into the EDID to begin reading (0..127)
  1467. * buffer - Location in which to return the EDID data
  1468. * buflen - Size of buffer in bytes
  1469. *
  1470. * Returned Value:
  1471. * On success, the number of bytes read is returned; a negated errno value
  1472. * is returned on any failure.
  1473. *
  1474. ****************************************************************************/
  1475. ssize_t tda19988_read_edid(TDA19988_HANDLE handle, off_t offset,
  1476. FAR uint8_t *buffer, size_t buflen)
  1477. {
  1478. FAR struct tda1988_dev_s *priv = (FAR struct tda1988_dev_s *)handle;
  1479. size_t nread;
  1480. int ret;
  1481. DEBUGASSERT(priv != NULL);
  1482. /* Get exclusive access to the driver */
  1483. ret = nxsem_wait(&priv->exclsem);
  1484. if (ret < 0)
  1485. {
  1486. return ret;
  1487. }
  1488. /* Defer the heavy lifting to tda19988_read_internal() */
  1489. nread = tda19988_read_internal(priv, offset, buffer, buflen);
  1490. if (nread < 0)
  1491. {
  1492. lcderr("ERROR: tda19988_read_internal failed: %d\n",
  1493. (int)nread);
  1494. }
  1495. nxsem_post(&priv->exclsem);
  1496. return nread;
  1497. }