On 04/09/18 14:16, Russell King wrote:
> The TDA998x is a HDMI transmitter with a TDA9950 CEC engine integrated
> onto the same die. Add support for the TDA9950 CEC engine to the
> TDA998x driver.
>
> Signed-off-by: Russell King
Reviewed-by: Hans Verkuil
Regards,
Hans
> ---
> drivers/gpu/drm/i2c/Kconfig | 1 +
> drivers/gpu/drm/i2c/tda998x_drv.c | 195
> --
> 2 files changed, 187 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig
> index 3a232f5ff0a1..65d3acb61c03 100644
> --- a/drivers/gpu/drm/i2c/Kconfig
> +++ b/drivers/gpu/drm/i2c/Kconfig
> @@ -22,6 +22,7 @@ config DRM_I2C_SIL164
> config DRM_I2C_NXP_TDA998X
> tristate "NXP Semiconductors TDA998X HDMI encoder"
> default m if DRM_TILCDC
> + select CEC_CORE if CEC_NOTIFIER
> select SND_SOC_HDMI_CODEC if SND_SOC
> help
> Support for NXP Semiconductors TDA998X HDMI encoders.
> diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c
> b/drivers/gpu/drm/i2c/tda998x_drv.c
> index 16e0439cad44..eb9916bd84a4 100644
> --- a/drivers/gpu/drm/i2c/tda998x_drv.c
> +++ b/drivers/gpu/drm/i2c/tda998x_drv.c
> @@ -16,8 +16,10 @@
> */
>
> #include
> +#include
> #include
> #include
> +#include
> #include
> #include
> #include
> @@ -29,6 +31,8 @@
> #include
> #include
>
> +#include
> +
> #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
>
> struct tda998x_audio_port {
> @@ -55,6 +59,7 @@ struct tda998x_priv {
> struct platform_device *audio_pdev;
> struct mutex audio_mutex;
>
> + struct mutex edid_mutex;
> wait_queue_head_t wq_edid;
> volatile int wq_edid_wait;
>
> @@ -67,6 +72,9 @@ struct tda998x_priv {
> struct drm_connector connector;
>
> struct tda998x_audio_port audio_port[2];
> + struct tda9950_glue cec_glue;
> + struct gpio_desc *calib;
> + struct cec_notifier *cec_notify;
> };
>
> #define conn_to_tda998x_priv(x) \
> @@ -345,6 +353,12 @@ struct tda998x_priv {
> #define REG_CEC_INTSTATUS 0xee/* read */
> # define CEC_INTSTATUS_CEC (1 << 0)
> # define CEC_INTSTATUS_HDMI(1 << 1)
> +#define REG_CEC_CAL_XOSC_CTRL10xf2
> +# define CEC_CAL_XOSC_CTRL1_ENA_CAL BIT(0)
> +#define REG_CEC_DES_FREQ2 0xf5
> +# define CEC_DES_FREQ2_DIS_AUTOCAL BIT(7)
> +#define REG_CEC_CLK 0xf6
> +# define CEC_CLK_FRO 0x11
> #define REG_CEC_FRO_IM_CLK_CTRL 0xfb/* read/write */
> # define CEC_FRO_IM_CLK_CTRL_GHOST_DIS (1 << 7)
> # define CEC_FRO_IM_CLK_CTRL_ENA_OTP (1 << 6)
> @@ -359,6 +373,7 @@ struct tda998x_priv {
> # define CEC_RXSHPDLEV_HPD(1 << 1)
>
> #define REG_CEC_ENAMODS 0xff/* read/write */
> +# define CEC_ENAMODS_EN_CEC_CLK (1 << 7)
> # define CEC_ENAMODS_DIS_FRO (1 << 6)
> # define CEC_ENAMODS_DIS_CCLK (1 << 5)
> # define CEC_ENAMODS_EN_RXSENS(1 << 2)
> @@ -417,6 +432,114 @@ cec_read(struct tda998x_priv *priv, u8 addr)
> return val;
> }
>
> +static void cec_enamods(struct tda998x_priv *priv, u8 mods, bool enable)
> +{
> + int val = cec_read(priv, REG_CEC_ENAMODS);
> +
> + if (val < 0)
> + return;
> +
> + if (enable)
> + val |= mods;
> + else
> + val &= ~mods;
> +
> + cec_write(priv, REG_CEC_ENAMODS, val);
> +}
> +
> +static void tda998x_cec_set_calibration(struct tda998x_priv *priv, bool
> enable)
> +{
> + if (enable) {
> + u8 val;
> +
> + cec_write(priv, 0xf3, 0xc0);
> + cec_write(priv, 0xf4, 0xd4);
> +
> + /* Enable automatic calibration mode */
> + val = cec_read(priv, REG_CEC_DES_FREQ2);
> + val &= ~CEC_DES_FREQ2_DIS_AUTOCAL;
> + cec_write(priv, REG_CEC_DES_FREQ2, val);
> +
> + /* Enable free running oscillator */
> + cec_write(priv, REG_CEC_CLK, CEC_CLK_FRO);
> + cec_enamods(priv, CEC_ENAMODS_DIS_FRO, false);
> +
> + cec_write(priv, REG_CEC_CAL_XOSC_CTRL1,
> + CEC_CAL_XOSC_CTRL1_ENA_CAL);
> + } else {
> + cec_write(priv, REG_CEC_CAL_XOSC_CTRL1, 0);
> + }
> +}
> +
> +/*
> + * Calibration for the internal oscillator: we need to set calibration mode,
> + * and then pulse the IRQ line low for a 10ms ± 1% period.
> + */
> +static void tda998x_cec_calibration(struct tda998x_priv *priv)
> +{
> + struct gpio_desc *calib = priv->calib;
> +
> + mutex_lock(>edid_mutex);
> + if (priv->hdmi->irq > 0)
> + disable_irq(priv->hdmi->irq);
> + gpiod_direction_output(calib, 1);
> + tda998x_cec_set_calibration(priv, true);
> +
> + local_irq_disable();
> + gpiod_set_value(calib, 0);
> + mdelay(10);
> + gpiod_set_value(calib, 1);
> + local_irq_enable();
> +
> +