12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823 |
- /****************************************************************************
- * drivers/lcd/tda19988.c
- *
- * Copyright (C) 2019 Gregory Nutt. All rights reserved.
- * Author: Gregory Nutt <gnutt@nuttx.org>
- *
- * Derives rather loosely from the FreeBSD driver which has a compatible
- * two-clause BSD license:
- *
- * Copyright (c) 2015 Oleksandr Tymoshenko <gonzo@freebsd.org>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in
- * the documentation and/or other materials provided with the
- * distribution.
- * 3. Neither the name NuttX nor the names of its contributors may be
- * used to endorse or promote products derived from this software
- * without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
- * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
- * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
- * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
- * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
- * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
- * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- * POSSIBILITY OF SUCH DAMAGE.
- *
- ****************************************************************************/
- /****************************************************************************
- * Included Files
- ****************************************************************************/
- #include <nuttx/config.h>
- #include <sys/types.h>
- #include <stdbool.h>
- #include <string.h>
- #include <poll.h>
- #include <assert.h>
- #include <errno.h>
- #include <nuttx/kmalloc.h>
- #include <nuttx/semaphore.h>
- #include <nuttx/fs/fs.h>
- #include <nuttx/drivers/drivers.h>
- #include <nuttx/video/edid.h>
- #include <nuttx/lcd/tda19988.h>
- #include "tda19988.h"
- /****************************************************************************
- * Pre-processor Definitions
- ****************************************************************************/
- /* Returned values from tda19988_connected() */
- #define DISPLAY_CONNECTED 0
- #define DISPLAY_DETACHED 1
- /* Number of times to try reading EDID */
- #define MAX_READ_ATTEMPTS 100
- #define HDMI_CTRL_CEC_ENAMODS 0xff
- /****************************************************************************
- * Private Types
- ****************************************************************************/
- /* This structure represents the state of one TDA19988 driver instance */
- struct tda1988_dev_s
- {
- /* The contained lower half driver instance */
- FAR const struct tda19988_lower_s *lower;
- /* Upper half driver state */
- sem_t exclsem; /* Assures exclusive access to the driver */
- uint8_t page; /* Currently selected page */
- uint8_t crefs; /* Number of open references */
- #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
- bool unlinked; /* True, driver has been unlinked */
- #endif
- uint16_t version; /* TDA19988 version */
- FAR uint8_t *edid; /* Extended Display Identification Data */
- uint32_t edid_len; /* Size of EDID */
- };
- /****************************************************************************
- * Private Function Prototypes
- ****************************************************************************/
- /* General I2C Helpers */
- static int tda19988_getregs(FAR const struct tda19988_i2c_s *dev,
- uint8_t regaddr, FAR uint8_t *regval, int nregs);
- static int tda19988_putreg(FAR const struct tda19988_i2c_s *dev,
- uint8_t regaddr, uint8_t regval);
- static int tda19988_putreg16(FAR const struct tda19988_i2c_s *dev,
- uint8_t regaddr, uint16_t regval);
- static int tda19988_modifyreg(FAR const struct tda19988_i2c_s *dev,
- uint8_t regaddr, uint8_t clrbits, uint8_t setbits);
- /* CEC I2C Helpers */
- static inline int tda19988_cec_getregs(FAR struct tda1988_dev_s *priv,
- uint8_t regaddr, FAR uint8_t *regval, int nregs);
- static inline int tda19988_cec_putreg(FAR struct tda1988_dev_s *priv,
- uint8_t regaddr, uint8_t regval);
- static inline int tda19988_cec_modifyreg(FAR struct tda1988_dev_s *priv,
- uint8_t regaddr, uint8_t clrbits, uint8_t setbits);
- /* HDMI I2C Helpers */
- static int tda19988_select_page(FAR struct tda1988_dev_s *priv,
- uint8_t page);
- static int tda19988_hdmi_getregs(FAR struct tda1988_dev_s *priv,
- uint16_t reginfo, FAR uint8_t *regval, int nregs);
- static int tda19988_hdmi_putreg(FAR struct tda1988_dev_s *priv,
- uint16_t reginfo, uint8_t regval);
- static int tda19988_hdmi_putreg16(FAR struct tda1988_dev_s *priv,
- uint16_t reginfo, uint16_t regval);
- static int tda19988_hdmi_modifyreg(FAR struct tda1988_dev_s *priv,
- uint16_t reginfo, uint8_t clrbits, uint8_t setbits);
- /* CEC Module Helpers */
- #if 0 /* Not used */
- static int tda19988_connected(FAR struct tda1988_dev_s *priv);
- #endif
- /* HDMI Module Helpers */
- static int tda19988_fetch_edid_block(FAR struct tda1988_dev_s *priv,
- FAR uint8_t *buf, int block);
- static int tda19988_fetch_edid(struct tda1988_dev_s *priv);
- static ssize_t tda19988_read_internal(FAR struct tda1988_dev_s *priv,
- off_t offset, FAR uint8_t *buffer, size_t buflen);
- /* Character driver methods */
- static int tda19988_open(FAR struct file *filep);
- static int tda19988_close(FAR struct file *filep);
- static ssize_t tda19988_read(FAR struct file *filep, FAR char *buffer,
- size_t buflen);
- static ssize_t tda19988_write(FAR struct file *filep, FAR const char *buffer,
- size_t buflen);
- static off_t tda19988_seek(FAR struct file *filep, off_t offset,
- int whence);
- static int tda19988_ioctl(FAR struct file *filep, int cmd,
- unsigned long arg);
- static int tda19988_poll(FAR struct file *filep, FAR struct pollfd *fds,
- bool setup);
- #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
- static int tda19988_unlink(FAR struct inode *inode);
- #endif
- /* Initialization */
- static int tda19988_hwinitialize(FAR struct tda1988_dev_s *priv);
- static int tda19988_videomode_internal(FAR struct tda1988_dev_s *priv,
- FAR const struct videomode_s *mode);
- #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
- static void tda19988_shutdown(FAR struct tda1988_dev_s *priv);
- #endif
- /****************************************************************************
- * Private Data
- ****************************************************************************/
- static const struct file_operations tda19988_fops =
- {
- tda19988_open, /* open */
- tda19988_close, /* close */
- tda19988_read, /* read */
- tda19988_write, /* write */
- tda19988_seek, /* seek */
- tda19988_ioctl, /* ioctl */
- tda19988_poll /* poll */
- #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
- , tda19988_unlink /* unlink */
- #endif
- };
- /****************************************************************************
- * Private Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: tda19988_getregs
- *
- * Description:
- * Read the value from one or more TDA19988 CEC or HDMI registers
- *
- * Returned Value:
- * Zero (OK) is returned on success; otherwise a negated errno value is
- * returned.
- *
- ****************************************************************************/
- static int tda19988_getregs(FAR const struct tda19988_i2c_s *dev,
- uint8_t regaddr, FAR uint8_t *regval, int nregs)
- {
- uint8_t buffer[1];
- int ret;
- DEBUGASSERT(dev != NULL && regval != NULL && nregs > 0);
- /* Write the register address and read the register value */
- buffer[0] = regaddr;
- ret = i2c_writeread(dev->i2c, &dev->config, buffer, 1, regval, nregs);
- if (ret < 0)
- {
- lcderr("ERROR: i2c_writeread() failed: %d\n", ret);
- return -1;
- }
- lcdinfo("Write: %02x<-%02x\n", regaddr, *regval);
- lcderrdumpbuffer("Read:", regval, nregs);
- return OK;
- }
- /****************************************************************************
- * Name: tda19988_putreg
- *
- * Description:
- * Write an 8-bit value to one TDA19988 CEC or HDMI register
- *
- * Returned Value:
- * Zero (OK) is returned on success; otherwise a negated errno value is
- * returned.
- *
- ****************************************************************************/
- static int tda19988_putreg(FAR const struct tda19988_i2c_s *dev,
- uint8_t regaddr, uint8_t regval)
- {
- uint8_t buffer[2];
- int ret;
- /* Write the register address and the register value */
- buffer[0] = regaddr;
- buffer[1] = regval;
- ret = i2c_write(dev->i2c, &dev->config, buffer, 2);
- if (ret < 0)
- {
- lcderr("ERROR: i2c_write() failed: %d\n", ret);
- return ret;
- }
- lcdinfo("Wrote: %02x<-%02x\n", regaddr, regval);
- return OK;
- }
- /****************************************************************************
- * Name: tda19988_putreg16
- *
- * Description:
- * Write a 16-bit value to one TDA19988 CEC or HDMI register
- *
- * Returned Value:
- * Zero (OK) is returned on success; otherwise a negated errno value is
- * returned.
- *
- ****************************************************************************/
- static int tda19988_putreg16(FAR const struct tda19988_i2c_s *dev,
- uint8_t regaddr, uint16_t regval)
- {
- uint8_t buffer[3];
- int ret;
- /* Write the register address and the register value */
- buffer[0] = regaddr;
- buffer[1] = (regval >> 8);
- buffer[2] = (regval & 0xff);
- ret = i2c_write(dev->i2c, &dev->config, buffer, 3);
- if (ret < 0)
- {
- lcderr("ERROR: i2c_write() failed: %d\n", ret);
- return ret;
- }
- lcdinfo("Wrote: 02x<-%04x\n", regaddr, regval);
- return OK;
- }
- /****************************************************************************
- * Name: tda19988_modifyreg
- *
- * Description:
- * Modify bits in one TDA19988 CEC or HDMI register
- *
- * Returned Value:
- * Zero (OK) is returned on success; otherwise a negated errno value is
- * returned.
- *
- ****************************************************************************/
- static int tda19988_modifyreg(FAR const struct tda19988_i2c_s *dev,
- uint8_t regaddr, uint8_t clrbits,
- uint8_t setbits)
- {
- uint8_t regval;
- int ret;
- /* Read the register contents */
- ret = tda19988_getregs(dev, regaddr, ®val, 1);
- if (ret < 0)
- {
- lcderr("ERROR: tda19988_getregs failed: %d\n", ret);
- return ret;
- }
- /* Modify the register content */
- regval &= ~clrbits;
- regval |= setbits;
- /* Write the modified register content */
- ret = tda19988_putreg(dev, regaddr, regval);
- if (ret < 0)
- {
- lcderr("ERROR: tda19988_putreg failed: %d\n", ret);
- return ret;
- }
- return OK;
- }
- /****************************************************************************
- * Name: tda19988_cec_getregs
- *
- * Description:
- * Read the value from one or more TDA19988 CEC registers
- *
- * Returned Value:
- * Zero (OK) is returned on success; otherwise a negated errno value is
- * returned.
- *
- ****************************************************************************/
- static inline int tda19988_cec_getregs(FAR struct tda1988_dev_s *priv,
- uint8_t regaddr, FAR uint8_t *regval,
- int nregs)
- {
- return tda19988_getregs(&priv->lower->cec, regaddr, regval, nregs);
- }
- /****************************************************************************
- * Name: tda19988_cec_putreg
- *
- * Description:
- * Write a value to one TDA19988 CEC register
- *
- * Returned Value:
- * Zero (OK) is returned on success; otherwise a negated errno value is
- * returned.
- *
- ****************************************************************************/
- static inline int tda19988_cec_putreg(FAR struct tda1988_dev_s *priv,
- uint8_t regaddr, uint8_t regval)
- {
- return tda19988_putreg(&priv->lower->cec, regaddr, regval);
- }
- /****************************************************************************
- * Name: tda19988_cec_modifyreg
- *
- * Description:
- * Modify bits in one TDA19988 CEC register
- *
- * Returned Value:
- * Zero (OK) is returned on success; otherwise a negated errno value is
- * returned.
- *
- ****************************************************************************/
- static inline int tda19988_cec_modifyreg(FAR struct tda1988_dev_s *priv,
- uint8_t regaddr, uint8_t clrbits,
- uint8_t setbits)
- {
- return tda19988_modifyreg(&priv->lower->cec, regaddr, clrbits, setbits);
- }
- /****************************************************************************
- * Name: tda19988_select_page
- *
- * Description:
- * Select the HDMI page (if not already selected)
- *
- * Returned Value:
- * Zero (OK) is returned on success; otherwise a negated errno value is
- * returned.
- *
- ****************************************************************************/
- static int tda19988_select_page(FAR struct tda1988_dev_s *priv, uint8_t page)
- {
- int ret = OK;
- /* Check if we need to select a new page for this transfer */
- if (page != HDMI_NO_PAGE && page != priv->page)
- {
- ret = tda19988_putreg(&priv->lower->hdmi,
- REGADDR(HDMI_PAGE_SELECT_REG), page);
- }
- return ret;
- }
- /****************************************************************************
- * Name: tda19988_hdmi_getregs
- *
- * Description:
- * Read the value from one or more TDA19988 HDMI registers
- *
- * Returned Value:
- * Zero (OK) is returned on success; otherwise a negated errno value is
- * returned.
- *
- ****************************************************************************/
- static int tda19988_hdmi_getregs(FAR struct tda1988_dev_s *priv,
- uint16_t reginfo, FAR uint8_t *regval,
- int nregs)
- {
- uint8_t page = REGPAGE(reginfo);
- uint8_t regaddr = REGADDR(reginfo);
- int ret;
- DEBUGASSERT(priv != NULL && regval != NULL && nregs > 0);
- /* Select the HDMI page */
- ret = tda19988_select_page(priv, page);
- if (ret < 0)
- {
- lcderr("ERROR: Failed to select page %02x: %d\n", page, ret);
- return ret;
- }
- /* Write the register address and read the register value */
- ret = tda19988_getregs(&priv->lower->hdmi, regaddr, regval, nregs);
- if (ret < 0)
- {
- lcderr("ERROR: tda19988_getregs() failed: %d\n", ret);
- return -1;
- }
- lcdinfo("Read: %02x:%02x->%02x\n", page, regaddr, *regval);
- return OK;
- }
- /****************************************************************************
- * Name: tda19988_hdmi_putreg
- *
- * Description:
- * Write an 8-bit value to one TDA19988 HDMI register
- *
- * Returned Value:
- * Zero (OK) is returned on success; otherwise a negated errno value is
- * returned.
- *
- ****************************************************************************/
- static int tda19988_hdmi_putreg(FAR struct tda1988_dev_s *priv,
- uint16_t reginfo, uint8_t regval)
- {
- uint8_t page = REGPAGE(reginfo);
- uint8_t regaddr = REGADDR(reginfo);
- int ret;
- /* Select the HDMI page */
- ret = tda19988_select_page(priv, page);
- if (ret < 0)
- {
- lcderr("ERROR: tda19988_select_page failed page %02x: %d\n",
- page, ret);
- return ret;
- }
- /* Write the register address and the register value */
- ret = tda19988_putreg(&priv->lower->hdmi, regaddr, regval);
- if (ret < 0)
- {
- lcderr("ERROR: tda19988_putreg() failed: %d\n", ret);
- return ret;
- }
- lcdinfo("Read: %02x:%02x<-%02x\n", page, regaddr, regval);
- return OK;
- }
- /****************************************************************************
- * Name: tda19988_hdmi_putreg16
- *
- * Description:
- * Write a 16-bit value to one TDA19988 HDMI register
- *
- * Returned Value:
- * Zero (OK) is returned on success; otherwise a negated errno value is
- * returned.
- *
- ****************************************************************************/
- static int tda19988_hdmi_putreg16(FAR struct tda1988_dev_s *priv,
- uint16_t reginfo, uint16_t regval)
- {
- uint8_t page = REGPAGE(reginfo);
- uint8_t regaddr = REGADDR(reginfo);
- int ret;
- /* Select the HDMI page */
- ret = tda19988_select_page(priv, page);
- if (ret < 0)
- {
- lcderr("ERROR: tda19988_select_page failed page %02x: %d\n",
- page, ret);
- return ret;
- }
- /* Write the register address and the register value */
- ret = tda19988_putreg16(&priv->lower->hdmi, regaddr, regval);
- if (ret < 0)
- {
- lcderr("ERROR: tda19988_putreg16() failed: %d\n", ret);
- return ret;
- }
- lcdinfo("Read: %02x:%02x<-%04x\n", page, regaddr, regval);
- return OK;
- }
- /****************************************************************************
- * Name: tda19988_hdmi_modifyreg
- *
- * Description:
- * Modify bits in one TDA19988 HDMI register
- *
- * Returned Value:
- * Zero (OK) is returned on success; otherwise a negated errno value is
- * returned.
- *
- ****************************************************************************/
- static int tda19988_hdmi_modifyreg(FAR struct tda1988_dev_s *priv,
- uint16_t reginfo, uint8_t clrbits,
- uint8_t setbits)
- {
- uint8_t page = REGPAGE(reginfo);
- uint8_t regaddr = REGADDR(reginfo);
- int ret;
- /* Select the HDMI page */
- ret = tda19988_select_page(priv, page);
- if (ret < 0)
- {
- lcderr("ERROR: Failed to select page %02x: %d\n", page, ret);
- return ret;
- }
- /* Read-modify-write the register contents */
- ret = tda19988_modifyreg(&priv->lower->hdmi, regaddr, clrbits, setbits);
- if (ret < 0)
- {
- lcderr("ERROR: tda19988_modifyreg failed: %d\n", ret);
- return ret;
- }
- return OK;
- }
- /****************************************************************************
- * Name: tda19988_connected
- *
- * Description:
- * Check if a display is connected.
- *
- * Returned Values:
- * DISPLAY_CONNECTED - A display is connected
- * DISPLAY_DETACHED - No display is connected
- * A negated errno value is returned on any failure.
- *
- ****************************************************************************/
- #if 0 /* Not used */
- static int tda19988_connected(FAR struct tda1988_dev_s *priv)
- {
- uint8_t regval;
- int ret;
- ret = tda19988_cec_getregs(priv, CEC_STATUS_REG, ®val, 1);
- if (ret < 0)
- {
- lcderr("ERROR: tda19988_cec_getregs failed: %d\n", ret);
- return ret;
- }
- if ((regval & CEC_STATUS_CONNECTED) == 0)
- {
- lcdwarn("WARNING: Display not connected\n");
- return DISPLAY_DETACHED;
- }
- else
- {
- lcdinfo("Display connect\n");
- return DISPLAY_CONNECTED;
- }
- }
- #endif
- /****************************************************************************
- * Name: tda19988_fetch_edid_block
- *
- * Description:
- * Fetch one EDID block from the DSD.
- *
- * Returned Value:
- * Zero (OK) is returned on success; A negated errno value is returned on
- * any failure.
- *
- ****************************************************************************/
- static int tda19988_fetch_edid_block(FAR struct tda1988_dev_s *priv,
- FAR uint8_t *buf, int block)
- {
- uint8_t data;
- int attempt;
- int ret;
- ret = OK;
- tda19988_hdmi_modifyreg(priv, HDMI_CTRL_INT_REG, 0, HDMI_CTRL_INT_EDID);
- /* Block 0 */
- tda19988_hdmi_putreg(priv, HDMI_EDID_DEV_ADDR_REG, 0xa0);
- tda19988_hdmi_putreg(priv, HDMI_EDID_OFFSET_REG,
- (block & 1) != 0 ? 128 : 0);
- tda19988_hdmi_putreg(priv, HDMI_EDID_SEGM_ADDR_REG, 0x60);
- tda19988_hdmi_putreg(priv, HDMI_EDID_DDC_SEGM_REG, block >> 1);
- tda19988_hdmi_putreg(priv, HDMI_EDID_REQ_REG, HDMI_EDID_REQ_READ);
- tda19988_hdmi_putreg(priv, HDMI_EDID_REQ_REG, 0);
- data = 0;
- for (attempt = 0; attempt < MAX_READ_ATTEMPTS; attempt++)
- {
- tda19988_hdmi_getregs(priv, HDMI_CTRL_INT_REG, &data, 1);
- if ((data & HDMI_CTRL_INT_EDID) != 0)
- {
- break;
- }
- }
- if (attempt == MAX_READ_ATTEMPTS)
- {
- ret = -ETIMEDOUT;
- goto done;
- }
- if (tda19988_hdmi_getregs(priv, HDMI_EDID_DATA_REG, buf, EDID_LENGTH) != 0)
- {
- ret = -EIO;
- goto done;
- }
- done:
- tda19988_hdmi_modifyreg(priv, HDMI_CTRL_INT_REG, HDMI_CTRL_INT_EDID, 0);
- return ret;
- }
- /****************************************************************************
- * Name: tda19988_fetch_edid
- *
- * Description:
- * Fetch the EDID block from the DSD.
- *
- * Returned Value:
- * Zero (OK) is returned on success; A negated errno value is returned on
- * any failure.
- *
- ****************************************************************************/
- static int tda19988_fetch_edid(struct tda1988_dev_s *priv)
- {
- int blocks;
- int ret;
- ret = 0;
- if (priv->version == HDMI_CTRL_REV_TDA19988)
- {
- tda19988_hdmi_modifyreg(priv, HDMI_HDCPOTP_TX4_REG,
- HDMI_HDCPOTP_TX4_PDRAM, 0);
- }
- ret = tda19988_fetch_edid_block(priv, priv->edid, 0);
- if (ret < 0)
- {
- goto done;
- }
- blocks = priv->edid[EDID_TRAILER_NEXTENSIONS_OFFSET];
- if (blocks > 0)
- {
- FAR uint8_t *edid;
- unsigned int edid_len;
- int i;
- edid_len = EDID_LENGTH * (blocks + 1);
- edid = (FAR void *)kmm_realloc(priv->edid, edid_len);
- if (edid == NULL)
- {
- lcderr("ERROR: Failed to kmm_realloc EDID\n");
- ret = -ENOMEM;
- goto done;
- }
- priv->edid = edid;
- priv->edid_len = edid_len;
- for (i = 0; i < blocks; i++)
- {
- FAR uint8_t *buf;
- /* TODO: check validity */
- buf = priv->edid + EDID_LENGTH * (i + 1);
- ret = tda19988_fetch_edid_block(priv, buf, i);
- if (ret < 0)
- {
- lcderr("ERROR: tda19988_fetch_edid_block failed: %d\n", ret);
- goto done;
- }
- }
- }
- done:
- if (priv->version == HDMI_CTRL_REV_TDA19988)
- {
- tda19988_hdmi_modifyreg(priv, HDMI_HDCPOTP_TX4_REG,
- 0, HDMI_HDCPOTP_TX4_PDRAM);
- }
- return ret;
- }
- /****************************************************************************
- * Name: tda19988_read_internal
- *
- * Description:
- * Return the previously read EDID data.
- *
- * Returned Value:
- * The number of bytes actually read is returned on success; A negated
- * errno value is returned on any failure.
- *
- ****************************************************************************/
- static ssize_t tda19988_read_internal(FAR struct tda1988_dev_s *priv,
- off_t offset, FAR uint8_t *buffer,
- size_t buflen)
- {
- DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
- /* Check if the offset lies outside of the EDID buffer */
- DEBUGASSERT(priv->edid != NULL && priv->edid_len > 0);
- if (offset < 0)
- {
- offset = 0;
- }
- else if (offset >= priv->edid_len)
- {
- return 0; /* End-of-file */
- }
- /* Clip the number of bytes so that the read region is wholly
- * within the EDID buffer.
- */
- if (offset + buflen > priv->edid_len)
- {
- buflen = priv->edid_len - offset;
- }
- memcpy(buffer, &priv->edid[offset], buflen);
- return (ssize_t)buflen;
- }
- /****************************************************************************
- * Name: tda19988_open
- *
- * Description:
- * Standard character driver open method.
- *
- * Returned Value:
- * Zero (OK) is returned on success; A negated errno value is returned on
- * any failure.
- *
- ****************************************************************************/
- static int tda19988_open(FAR struct file *filep)
- {
- FAR struct inode *inode;
- FAR struct tda1988_dev_s *priv;
- int ret;
- /* Get the private driver state instance */
- DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
- inode = filep->f_inode;
- priv = (FAR struct tda1988_dev_s *)inode->i_private;
- DEBUGASSERT(priv != NULL);
- /* Get exclusive access to the driver instance */
- ret = nxsem_wait(&priv->exclsem);
- if (ret < 0)
- {
- return ret;
- }
- /* Increment the reference count on the driver instance */
- DEBUGASSERT(priv->crefs != UINT8_MAX);
- priv->crefs++;
- nxsem_post(&priv->exclsem);
- return OK;
- }
- /****************************************************************************
- * Name: tda19988_close
- *
- * Description:
- * Standard character driver cl;ose method.
- *
- * Returned Value:
- * Zero (OK) is returned on success; A negated errno value is returned on
- * any failure.
- *
- ****************************************************************************/
- static int tda19988_close(FAR struct file *filep)
- {
- FAR struct inode *inode;
- FAR struct tda1988_dev_s *priv;
- int ret;
- /* Get the private driver state instance */
- DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
- inode = filep->f_inode;
- priv = (FAR struct tda1988_dev_s *)inode->i_private;
- DEBUGASSERT(priv != NULL);
- /* Get exclusive access to the driver */
- ret = nxsem_wait(&priv->exclsem);
- if (ret < 0)
- {
- return ret;
- }
- /* Decrement the count of open references on the driver */
- DEBUGASSERT(priv->crefs > 0);
- priv->crefs--;
- #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
- /* If the count has decremented to zero and the driver has been unlinked,
- * then self-destruct now.
- */
- if (priv->crefs == 0 && priv->unlinked)
- {
- tda19988_shutdown(priv);
- return OK;
- }
- #endif
- nxsem_post(&priv->exclsem);
- return -ENOSYS;
- }
- /****************************************************************************
- * Name: tda19988_read
- *
- * Description:
- * Standard character driver read method.
- *
- * Returned Value:
- * The number of bytes read is returned on success; A negated errno value
- * is returned on any failure. End-of-file (zero) is never returned.
- *
- ****************************************************************************/
- static ssize_t tda19988_read(FAR struct file *filep, FAR char *buffer,
- size_t len)
- {
- FAR struct inode *inode;
- FAR struct tda1988_dev_s *priv;
- ssize_t nread;
- int ret;
- /* Get the private driver state instance */
- DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
- inode = filep->f_inode;
- priv = (FAR struct tda1988_dev_s *)inode->i_private;
- DEBUGASSERT(priv != NULL);
- /* Get exclusive access to the driver */
- ret = nxsem_wait(&priv->exclsem);
- if (ret < 0)
- {
- return ret;
- }
- /* Return the previously read EDID data */
- nread = tda19988_read_internal(priv, filep->f_pos, (FAR uint8_t *)buffer,
- len);
- if (nread > 0)
- {
- filep->f_pos += nread;
- }
- nxsem_post(&priv->exclsem);
- return nread;
- }
- /****************************************************************************
- * Name: tda19988_write
- *
- * Description:
- * Standard character driver write method.
- *
- * Returned Value:
- * The number of bytes written is returned on success; A negated errno
- * value is returned on any failure.
- *
- ****************************************************************************/
- static ssize_t tda19988_write(FAR struct file *filep, FAR const char *buffer,
- size_t len)
- {
- /* Driver may be opened for write access. Writing, however, is not
- * supported.
- */
- return -ENOSYS;
- }
- /****************************************************************************
- * Name: tda19988_seek
- *
- * Description:
- * Standard character driver poll method.
- *
- * Returned Value:
- * The current file position is returned on success; A negated errno value
- * is returned on any failure.
- *
- ****************************************************************************/
- static off_t tda19988_seek(FAR struct file *filep, off_t offset, int whence)
- {
- FAR struct inode *inode;
- FAR struct tda1988_dev_s *priv;
- off_t pos;
- int ret;
- /* Get the private driver state instance */
- DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
- inode = filep->f_inode;
- priv = (FAR struct tda1988_dev_s *)inode->i_private;
- DEBUGASSERT(priv != NULL);
- /* Get exclusive access to the driver */
- ret = nxsem_wait(&priv->exclsem);
- if (ret < 0)
- {
- return ret;
- }
- /* Perform the seek operation */
- pos = filep->f_pos;
- switch (whence)
- {
- case SEEK_CUR:
- pos += offset;
- if (pos > EDID_LENGTH)
- {
- pos = EDID_LENGTH;
- }
- else if (pos < 0)
- {
- pos = 0;
- }
- filep->f_pos = pos;
- break;
- case SEEK_SET:
- pos = offset;
- if (pos > EDID_LENGTH)
- {
- pos = EDID_LENGTH;
- }
- else if (pos < 0)
- {
- pos = 0;
- }
- filep->f_pos = pos;
- break;
- case SEEK_END:
- pos = EDID_LENGTH + offset;
- if (pos > EDID_LENGTH)
- {
- pos = EDID_LENGTH;
- }
- else if (pos < 0)
- {
- pos = 0;
- }
- filep->f_pos = pos;
- break;
- default:
- /* Return EINVAL if the whence argument is invalid */
- pos = (off_t)-EINVAL;
- break;
- }
- nxsem_post(&priv->exclsem);
- return pos;
- }
- /****************************************************************************
- * Name: tda19988_ioctl
- *
- * Description:
- * Standard character driver poll method.
- *
- * Returned Value:
- * Zero (OK) is returned on success; A negated errno value is returned on
- * any failure.
- *
- ****************************************************************************/
- static int tda19988_ioctl(FAR struct file *filep, int cmd, unsigned long arg)
- {
- FAR struct inode *inode;
- FAR struct tda1988_dev_s *priv;
- int ret;
- /* Get the private driver state instance */
- DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
- inode = filep->f_inode;
- priv = (FAR struct tda1988_dev_s *)inode->i_private;
- DEBUGASSERT(priv != NULL);
- /* Get exclusive access to the driver */
- ret = nxsem_wait(&priv->exclsem);
- if (ret < 0)
- {
- return ret;
- }
- /* Handle the IOCTL command */
- switch (cmd)
- {
- /* TDA19988_IOC_VIDEOMODE:
- * Description: Select the video mode. This must be done as part
- * of the initialization of the driver. This is
- * equivalent to calling tda18899_videomode() within
- * the OS.
- * Argument: A reference to a videomode_s structure
- * instance.
- * Returns: None
- */
- case TDA19988_IOC_VIDEOMODE:
- {
- FAR const struct videomode_s *mode =
- (FAR const struct videomode_s *)((uintptr_t)arg);
- if (mode == NULL)
- {
- ret = -EINVAL;
- }
- else
- {
- ret = tda19988_videomode_internal(priv, mode);
- if (ret < 0)
- {
- lcderr("ERROR: tda19988_videomode_internal failed: %d\n",
- ret);
- }
- }
- }
- break;
- default:
- ret = -ENOTTY;
- break;
- }
- nxsem_post(&priv->exclsem);
- return ret;
- }
- /****************************************************************************
- * Name: tda19988_poll
- *
- * Description:
- * Standard character driver poll method.
- *
- * Returned Value:
- * Zero (OK) is returned on success; A negated errno value is returned on
- * any failure.
- *
- ****************************************************************************/
- static int tda19988_poll(FAR struct file *filep, FAR struct pollfd *fds,
- bool setup)
- {
- FAR struct inode *inode;
- FAR struct tda1988_dev_s *priv;
- int ret;
- /* Get the private driver state instance */
- DEBUGASSERT(filep != NULL && filep->f_inode != NULL);
- inode = filep->f_inode;
- priv = (FAR struct tda1988_dev_s *)inode->i_private;
- DEBUGASSERT(priv != NULL);
- /* Get exclusive access to the driver */
- ret = nxsem_wait(&priv->exclsem);
- if (ret < 0)
- {
- return ret;
- }
- if (setup)
- {
- fds->revents |= (fds->events & (POLLIN | POLLOUT));
- if (fds->revents != 0)
- {
- nxsem_post(fds->sem);
- }
- }
- nxsem_post(&priv->exclsem);
- return OK;
- }
- /****************************************************************************
- * Name: tda19988_unlink
- *
- * Description:
- * Standard character driver unlink method.
- *
- * Returned Value:
- * Zero (OK) is returned on success; A negated errno value is returned on
- * any failure.
- *
- ****************************************************************************/
- #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
- static int tda19988_unlink(FAR struct inode *inode)
- {
- FAR struct tda1988_dev_s *priv;
- int ret;
- /* Get the private driver state instance */
- DEBUGASSERT(inode != NULL && inode->i_private != NULL);
- priv = (FAR struct tda1988_dev_s *)inode->i_private;
- /* Get exclusive access to the driver */
- ret = nxsem_wait(&priv->exclsem);
- if (ret < 0)
- {
- return ret;
- }
- /* Are there open references to the driver data structure? */
- if (priv->crefs <= 0)
- {
- tda19988_shutdown(priv);
- return OK;
- }
- /* No... just mark the driver as unlinked and free the resources when the
- * last client closes their reference to the driver.
- */
- priv->unlinked = true;
- nxsem_post(&priv->exclsem);
- return OK;
- }
- #endif
- /****************************************************************************
- * Name: tda19988_hwinitialize
- *
- * Description:
- * Initialize the TDA19988 hardware.
- *
- * Input Parameters:
- * priv - TDA19988 driver state
- *
- * Returned Value:
- * Zero (OK) is returned on success; a negated errno value is returned on
- * any failure.
- *
- ****************************************************************************/
- static int tda19988_hwinitialize(FAR struct tda1988_dev_s *priv)
- {
- uint16_t version;
- uint8_t data;
- int ret;
- tda19988_cec_putreg(priv, CEC_ENAMODS_REG,
- CEC_ENAMODS_RXSENS | CEC_ENAMODS_HDMI);
- up_udelay(1000);
- tda19988_cec_getregs(priv, CEC_STATUS_REG, &data, 1);
- /* Reset core */
- tda19988_hdmi_modifyreg(priv, HDMI_CTRL_RESET_REG, 0, 3);
- up_udelay(100);
- tda19988_hdmi_modifyreg(priv, HDMI_CTRL_RESET_REG, 3, 0);
- up_udelay(100);
- /* Reset transmitter: */
- tda19988_hdmi_modifyreg(priv, HDMI_CTRL_MAIN_CNTRL0_REG, 0,
- HDMI_CTRL_MAIN_CNTRL0_SR);
- tda19988_hdmi_modifyreg(priv, HDMI_CTRL_MAIN_CNTRL0_REG,
- HDMI_CTRL_MAIN_CNTRL0_SR, 0);
- /* PLL registers common configuration */
- tda19988_hdmi_putreg(priv, HDMI_PLL_SERIAL_1_REG, 0x00);
- tda19988_hdmi_putreg(priv, HDMI_PLL_SERIAL_2_REG,
- HDMI_PLL_SERIAL_2_SRL_NOSC(1));
- tda19988_hdmi_putreg(priv, HDMI_PLL_SERIAL_3_REG, 0x00);
- tda19988_hdmi_putreg(priv, HDMI_PLL_SERIALIZER_REG, 0x00);
- tda19988_hdmi_putreg(priv, HDMI_PLL_BUFFER_OUT_REG, 0x00);
- tda19988_hdmi_putreg(priv, HDMI_PLL_SCG1_REG, 0x00);
- tda19988_hdmi_putreg(priv, HDMI_PLL_SEL_CLK_REG,
- HDMI_PLL_SEL_CLK_SEL_CLK1 |
- HDMI_PLL_SEL_CLK_ENA_SC_CLK);
- tda19988_hdmi_putreg(priv, HDMI_PLL_SCGN1_REG, 0xfa);
- tda19988_hdmi_putreg(priv, HDMI_PLL_SCGN2_REG, 0x00);
- tda19988_hdmi_putreg(priv, HDMI_PLL_SCGR1_REG, 0x5b);
- tda19988_hdmi_putreg(priv, HDMI_PLL_SCGR2_REG, 0x00);
- tda19988_hdmi_putreg(priv, HDMI_PLL_SCG2_REG, 0x10);
- /* Write the default value MUX register */
- tda19988_hdmi_putreg(priv, HDMI_CTRL_MUX_VP_VIP_OUT_REG, 0x24);
- tda19988_hdmi_getregs(priv, HDMI_CTRL_REV_LO_REG, &data, 1);
- version = (uint16_t)data;
- tda19988_hdmi_getregs(priv, HDMI_CTRL_REV_HI_REG, &data, 1);
- version |= ((uint16_t)data << 8);
- /* Clear feature bits */
- priv->version = version & ~0x30;
- switch (priv->version)
- {
- case HDMI_CTRL_REV_TDA19988:
- lcdinfo("TDA19988\n");
- break;
- default:
- lcderr("ERROR: Unknown device: %04x\n", priv->version);
- ret = -ENODEV;
- goto done;
- }
- tda19988_hdmi_putreg(priv, HDMI_CTRL_DDC_CTRL_REG, HDMI_CTRL_DDC_EN);
- tda19988_hdmi_putreg(priv, HDMI_HDCPOTP_TX3_REG, 39);
- tda19988_cec_putreg(priv, CEC_FRO_IM_CLK_CTRL_REG,
- CEC_FRO_IM_CLK_CTRL_GHOST_DIS |
- CEC_FRO_IM_CLK_CTRL_IMCLK_SEL);
- ret = tda19988_fetch_edid(priv);
- if (ret < 0)
- {
- lcderr("ERROR: tda19988_fetch_edid failed: %d\n", ret);
- goto done;
- }
- /* Default values for RGB 4:4:4 mapping */
- tda19988_hdmi_putreg(priv, HDMI_CTRL_VIPCTRL_0_REG, 0x23);
- tda19988_hdmi_putreg(priv, HDMI_CTRL_VIPCTRL_1_REG, 0x01);
- tda19988_hdmi_putreg(priv, HDMI_CTRL_VIPCTRL_2_REG, 0x45);
- ret = OK;
- done:
- return ret;
- }
- /****************************************************************************
- * Name: tda19988_videomode_internal
- *
- * Description:
- * Initialize the TDA19988 driver to a specified video mode. This is a
- * necessary part of the TDA19988 initialization: A video mode must be
- * configured before the driver is usable.
- *
- * Input Parameters:
- * priv - TDA19988 driver state
- * mode - The new video mode.
- *
- * Returned Value:
- * Zero (OK) is returned on success; a negated errno value is returned on
- * any failure.
- *
- ****************************************************************************/
- static int
- tda19988_videomode_internal(FAR struct tda1988_dev_s *priv,
- FAR const struct videomode_s *mode)
- {
- uint16_t ref_pix;
- uint16_t ref_line;
- uint16_t n_pix;
- uint16_t n_line;
- uint16_t hs_pix_start;
- uint16_t hs_pix_stop;
- uint16_t vs1_pix_start;
- uint16_t vs1_pix_stop;
- uint16_t vs1_line_start;
- uint16_t vs1_line_end;
- uint16_t vs2_pix_start;
- uint16_t vs2_pix_stop;
- uint16_t vs2_line_start;
- uint16_t vs2_line_end;
- uint16_t vwin1_line_start;
- uint16_t vwin1_line_end;
- uint16_t vwin2_line_start;
- uint16_t vwin2_line_end;
- uint16_t de_start;
- uint16_t de_stop;
- uint8_t regval;
- uint8_t div;
- DEBUGASSERT(priv != NULL && mode != NULL);
- n_pix = mode->htotal;
- n_line = mode->vtotal;
- hs_pix_stop = mode->hsync_end - mode->hdisplay;
- hs_pix_start = mode->hsync_start - mode->hdisplay;
- de_stop = mode->htotal;
- de_start = mode->htotal - mode->hdisplay;
- ref_pix = hs_pix_start + 3;
- if (mode->flags & VID_HSKEW)
- {
- ref_pix += mode->hskew;
- }
- if ((mode->flags & VID_INTERLACE) == 0)
- {
- ref_line = 1 + mode->vsync_start - mode->vdisplay;
- vwin1_line_start = mode->vtotal - mode->vdisplay - 1;
- vwin1_line_end = vwin1_line_start + mode->vdisplay;
- vs1_pix_start = vs1_pix_stop = hs_pix_start;
- vs1_line_start = mode->vsync_start - mode->vdisplay;
- vs1_line_end = vs1_line_start + mode->vsync_end -
- mode->vsync_start;
- vwin2_line_start = vwin2_line_end = 0;
- vs2_pix_start = vs2_pix_stop = 0;
- vs2_line_start = vs2_line_end = 0;
- }
- else
- {
- ref_line = 1 + (mode->vsync_start - mode->vdisplay) / 2;
- vwin1_line_start = (mode->vtotal - mode->vdisplay) / 2;
- vwin1_line_end = vwin1_line_start + mode->vdisplay / 2;
- vs1_pix_start = vs1_pix_stop = hs_pix_start;
- vs1_line_start = (mode->vsync_start - mode->vdisplay) / 2;
- vs1_line_end = vs1_line_start +
- (mode->vsync_end - mode->vsync_start) / 2;
- vwin2_line_start = vwin1_line_start + mode->vtotal / 2;
- vwin2_line_end = vwin2_line_start + mode->vdisplay / 2;
- vs2_pix_start = vs2_pix_stop = hs_pix_start + mode->htotal / 2;
- vs2_line_start = vs1_line_start + mode->vtotal / 2;
- vs2_line_end = vs2_line_start +
- (mode->vsync_end - mode->vsync_start) / 2;
- }
- div = 148500 / mode->dotclock;
- if (div != 0)
- {
- if (--div > 3)
- {
- div = 3;
- }
- }
- /* Set HDMI HDCP mode off */
- tda19988_hdmi_modifyreg(priv, HDMI_CTRL_TBG_CNTRL_1_REG, 0,
- HDMI_CTRL_TBG_CNTRL_1_DWIN_DIS);
- tda19988_hdmi_modifyreg(priv, HDMI_HDCPOTP_TX33_REG,
- HDMI_HDCPOTP_TX33_HDMI, 0);
- tda19988_hdmi_putreg(priv, HDMI_AUDIO_ENC_CTRL_REG,
- HDMI_AUDIO_ENC_CNTRL_DVI_MODE);
- /* No pre-filter or interpreter */
- tda19988_hdmi_putreg(priv, HDMI_CTRL_HVF_CNTRL_0_REG,
- HDMI_CTRL_HVF_CNTRL_0_INTPOL_BYPASS |
- HDMI_CTRL_HVF_CNTRL_0_PREFIL_NONE);
- tda19988_hdmi_putreg(priv, HDMI_CTRL_VIPCTRL_5_REG,
- HDMI_CTRL_VIPCTRL_5_SP_CNT(0));
- tda19988_hdmi_putreg(priv, HDMI_CTRL_VIPCTRL_4_REG,
- HDMI_CTRL_VIPCTRL_4_BLANKIT_NDE |
- HDMI_CTRL_VIPCTRL_4_BLC_NONE);
- tda19988_hdmi_modifyreg(priv, HDMI_PLL_SERIAL_3_REG,
- HDMI_PLL_SERIAL_3_SRL_CCIR, 0);
- tda19988_hdmi_modifyreg(priv, HDMI_PLL_SERIAL_1_REG,
- HDMI_PLL_SERIAL_1_SRL_MAN_IP, 0);
- tda19988_hdmi_modifyreg(priv, HDMI_PLL_SERIAL_3_REG,
- HDMI_PLL_SERIAL_3_SRL_DE, 0);
- tda19988_hdmi_putreg(priv, HDMI_PLL_SERIALIZER_REG, 0);
- tda19988_hdmi_putreg(priv, HDMI_CTRL_HVF_CNTRL_1_REG,
- HDMI_CTRL_HVF_CNTRL_1_VQR_FULL);
- tda19988_hdmi_putreg(priv, HDMI_CTRL_RPT_CNTRL_REG, 0);
- tda19988_hdmi_putreg(priv, HDMI_PLL_SEL_CLK_REG,
- HDMI_PLL_SEL_CLK_SEL_VRF_CLK(0) |
- HDMI_PLL_SEL_CLK_SEL_CLK1 |
- HDMI_PLL_SEL_CLK_ENA_SC_CLK);
- tda19988_hdmi_putreg(priv, HDMI_PLL_SERIAL_2_REG,
- HDMI_PLL_SERIAL_2_SRL_NOSC(div) |
- HDMI_PLL_SERIAL_2_SRL_PR(0));
- tda19988_hdmi_modifyreg(priv, HDMI_CTRL_MATCTRL_REG, 0,
- HDMI_CTRL_MAT_CONTRL_MAT_BP);
- tda19988_hdmi_putreg(priv, HDMI_PLL_ANA_GENERAL_REG, 0x09);
- tda19988_hdmi_modifyreg(priv, HDMI_CTRL_TBG_CNTRL_0_REG,
- HDMI_CTRL_TBG_CNTRL_0_SYNC_MTHD, 0);
- /* Sync on rising HSYNC/VSYNC */
- regval = HDMI_CTRL_VIPCTRL_3_SYNC_HS;
- if (mode->flags & VID_NHSYNC)
- {
- regval |= HDMI_CTRL_VIPCTRL_3_H_TGL;
- }
- if (mode->flags & VID_NVSYNC)
- {
- regval |= HDMI_CTRL_VIPCTRL_3_V_TGL;
- }
- tda19988_hdmi_putreg(priv, HDMI_CTRL_VIPCTRL_3_REG, regval);
- regval = HDMI_CTRL_TBG_CNTRL_1_TGL_EN;
- if (mode->flags & VID_NHSYNC)
- {
- regval |= HDMI_CTRL_TBG_CNTRL_1_H_TGL;
- }
- if (mode->flags & VID_NVSYNC)
- {
- regval |= HDMI_CTRL_TBG_CNTRL_1_V_TGL;
- }
- tda19988_hdmi_putreg(priv, HDMI_CTRL_TBG_CNTRL_1_REG, regval);
- /* Program timing */
- tda19988_hdmi_putreg(priv, HDMI_CTRL_MUX_VIDFORMAT_REG, 0x00);
- tda19988_hdmi_putreg16(priv, HDMI_CTRL_MUX_REFPIX_MSB_REG, ref_pix);
- tda19988_hdmi_putreg16(priv, HDMI_CTRL_MUX_REFLINE_MSB_REG, ref_line);
- tda19988_hdmi_putreg16(priv, HDMI_CTRL_MUX_NPIX_MSB_REG, n_pix);
- tda19988_hdmi_putreg16(priv, HDMI_CTRL_MUX_NLINE_MSB_REG, n_line);
- tda19988_hdmi_putreg16(priv, HDMI_CTRL_MUX_VS_LINE_STRT_1_MSB_REG,
- vs1_line_start);
- tda19988_hdmi_putreg16(priv, HDMI_CTRL_MUX_VS_PIX_STRT_1_MSB_REG,
- vs1_pix_start);
- tda19988_hdmi_putreg16(priv, HDMI_CTRL_VS_LINE_END_1_MSB_REG,
- vs1_line_end);
- tda19988_hdmi_putreg16(priv, HDMI_CTRL_VS_PIX_END_1_MSB_REG, vs1_pix_stop);
- tda19988_hdmi_putreg16(priv, HDMI_CTRL_VS_LINE_STRT_2_MSB_REG,
- vs2_line_start);
- tda19988_hdmi_putreg16(priv, HDMI_CTRL_VS_PIX_STRT_2_MSB_REG,
- vs2_pix_start);
- tda19988_hdmi_putreg16(priv, HDMI_CTRL_VS_LINE_END_2_MSB_REG,
- vs2_line_end);
- tda19988_hdmi_putreg16(priv, HDMI_CTRL_VS_PIX_END_2_MSB_REG, vs2_pix_stop);
- tda19988_hdmi_putreg16(priv, HDMI_CTRL_HS_PIX_START_MSB_REG, hs_pix_start);
- tda19988_hdmi_putreg16(priv, HDMI_CTRL_HS_PIX_STOP_MSB_REG, hs_pix_stop);
- tda19988_hdmi_putreg16(priv, HDMI_CTRL_VWIN_START_1_MSB_REG,
- vwin1_line_start);
- tda19988_hdmi_putreg16(priv, HDMI_CTRL_VWIN_END_1_MSB_REG, vwin1_line_end);
- tda19988_hdmi_putreg16(priv, HDMI_CTRL_VWIN_START_2_MSB_REG,
- vwin2_line_start);
- tda19988_hdmi_putreg16(priv, HDMI_CTRL_VWIN_END_2_MSB_REG, vwin2_line_end);
- tda19988_hdmi_putreg16(priv, HDMI_CTRL_DE_START_MSB_REG, de_start);
- tda19988_hdmi_putreg16(priv, HDMI_CTRL_DE_STOP_MSB_REG, de_stop);
- if (priv->version == HDMI_CTRL_REV_TDA19988)
- {
- tda19988_hdmi_putreg(priv, HDMI_CTRL_ENABLE_SPACE_REG, 0x00);
- }
- /* Must be last register set */
- tda19988_hdmi_modifyreg(priv, HDMI_CTRL_TBG_CNTRL_0_REG,
- HDMI_CTRL_TBG_CNTRL_0_SYNC_ONCE, 0);
- return OK;
- }
- /****************************************************************************
- * Name: tda19988_shutdown
- *
- * Description:
- * Free resources used by the driver when it has been unlinked.
- *
- * Returned Value:
- * None.
- *
- ****************************************************************************/
- #ifndef CONFIG_DISABLE_PSEUDOFS_OPERATIONS
- static void tda19988_shutdown(FAR struct tda1988_dev_s *priv)
- {
- /* Detach and disable interrupts */
- if (priv->lower != NULL) /* May be called before fully initialized */
- {
- DEBUGASSERT(priv->lower->attach != NULL &&
- priv->lower->enable != NULL);
- priv->lower->attach(priv->lower, NULL, NULL);
- priv->lower->enable(priv->lower, false);
- }
- /* Release resources */
- nxsem_destroy(&priv->exclsem);
- /* Free memory */
- if (priv->edid != NULL)
- {
- kmm_free(priv->edid);
- }
- kmm_free(priv);
- }
- #endif
- /****************************************************************************
- * Public Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: tda19988_register
- *
- * Description:
- * Create and register the the TDA19988 driver at 'devpath'
- *
- * Input Parameters:
- * devpath - The location to register the TDA19988 driver instance. The
- * standard location would be a path like /dev/hdmi0.
- * lower - The interface to the the TDA19988 lower half driver.
- *
- * Returned Value:
- * On success, non-NULL handle is returned that may be subsequently used
- * with tda19988_videomode(). NULL is returned on failure.
- *
- ****************************************************************************/
- TDA19988_HANDLE tda19988_register(FAR const char *devpath,
- FAR const struct tda19988_lower_s *lower)
- {
- FAR struct tda1988_dev_s *priv;
- int ret;
- DEBUGASSERT(devpath != NULL && lower != NULL);
- /* Allocate an instance of the TDA19988 driver */
- priv = (FAR struct tda1988_dev_s *)
- kmm_zalloc(sizeof(struct tda1988_dev_s));
- if (priv == NULL)
- {
- lcderr("ERROR: Failed to allocate device structure\n");
- return NULL;
- }
- /* Assume a single block in EDID */
- priv->edid = (FAR uint8_t *)kmm_malloc(EDID_LENGTH);
- if (priv->edid == NULL)
- {
- lcderr("ERROR: Failed to allocate EDID\n");
- tda19988_shutdown(priv);
- return NULL;
- }
- priv->edid_len = EDID_LENGTH;
- /* Initialize the driver structure */
- priv->lower = lower;
- priv->page = HDMI_NO_PAGE;
- nxsem_init(&priv->exclsem, 0, 1);
- /* Initialize the TDA19988 */
- ret = tda19988_hwinitialize(priv);
- if (ret < 0)
- {
- lcderr("ERROR: tda19988_hwinitialize failed: %d\n", ret);
- tda19988_shutdown(priv);
- return NULL;
- }
- /* Register the driver */
- ret = register_driver(devpath, &tda19988_fops, 0666, NULL);
- if (ret < 0)
- {
- lcderr("ERROR: register_driver() failed: %d\n", ret);
- tda19988_shutdown(priv);
- return NULL;
- }
- return (TDA19988_HANDLE)priv;
- }
- /****************************************************************************
- * Name: tda19988_videomode
- *
- * Description:
- * Initialize the TDA19988 driver to a specified video mode. This is a
- * necessary part of the TDA19988 initialization: A video mode must be
- * configured before the driver is usable.
- *
- * NOTE: This may be done in two ways: (1) via a call to
- * tda19988_videomode() from board-specific logic within the OS, or
- * equivalently (2) using the TDA19988_IOC_VIDEOMODE from application
- * logic outside of the OS.
- *
- * Input Parameters:
- * handle - The handle previously returned by tda19988_register().
- * mode - The new video mode.
- *
- * Returned Value:
- * Zero (OK) is returned on success; a negated errno value is returned on
- * any failure.
- *
- ****************************************************************************/
- int tda19988_videomode(TDA19988_HANDLE handle,
- FAR const struct videomode_s *mode)
- {
- FAR struct tda1988_dev_s *priv = (FAR struct tda1988_dev_s *)handle;
- int ret;
- DEBUGASSERT(priv != NULL && mode != NULL);
- /* Get exclusive access to the driver */
- ret = nxsem_wait(&priv->exclsem);
- if (ret < 0)
- {
- return ret;
- }
- /* Defer the heavy lifting to tda19988_videomode_internal() */
- ret = tda19988_videomode_internal(priv, mode);
- if (ret < 0)
- {
- lcderr("ERROR: tda19988_videomode_internal failed: %d\n", ret);
- }
- nxsem_post(&priv->exclsem);
- return ret;
- }
- /****************************************************************************
- * Name: tda19988_read_edid
- *
- * Description:
- * Read the EDID (Extended Display Identification Data).
- *
- * NOTE: This may be done in two ways: (1) via a call to
- * tda19988_read_edid() from board-specific logic within the OS, or
- * equivalently (2) using a standard read() to read the EDID from
- * application logic outside of the OS.
- *
- * Input Parameters:
- * handle - The handle previously returned by tda19988_register().
- * offset - The offset into the EDID to begin reading (0..127)
- * buffer - Location in which to return the EDID data
- * buflen - Size of buffer in bytes
- *
- * Returned Value:
- * On success, the number of bytes read is returned; a negated errno value
- * is returned on any failure.
- *
- ****************************************************************************/
- ssize_t tda19988_read_edid(TDA19988_HANDLE handle, off_t offset,
- FAR uint8_t *buffer, size_t buflen)
- {
- FAR struct tda1988_dev_s *priv = (FAR struct tda1988_dev_s *)handle;
- size_t nread;
- int ret;
- DEBUGASSERT(priv != NULL);
- /* Get exclusive access to the driver */
- ret = nxsem_wait(&priv->exclsem);
- if (ret < 0)
- {
- return ret;
- }
- /* Defer the heavy lifting to tda19988_read_internal() */
- nread = tda19988_read_internal(priv, offset, buffer, buflen);
- if (nread < 0)
- {
- lcderr("ERROR: tda19988_read_internal failed: %d\n",
- (int)nread);
- }
- nxsem_post(&priv->exclsem);
- return nread;
- }
|