linux/drivers/gpu/drm/i2c/tda998x_drv.c

// SPDX-License-Identifier: GPL-2.0-only
/*
 * Copyright (C) 2012 Texas Instruments
 * Author: Rob Clark <[email protected]>
 */

#include <linux/component.h>
#include <linux/gpio/consumer.h>
#include <linux/hdmi.h>
#include <linux/i2c.h>
#include <linux/module.h>
#include <linux/platform_data/tda9950.h>
#include <linux/irq.h>
#include <sound/asoundef.h>
#include <sound/hdmi-codec.h>

#include <drm/drm_atomic_helper.h>
#include <drm/drm_bridge.h>
#include <drm/drm_edid.h>
#include <drm/drm_of.h>
#include <drm/drm_print.h>
#include <drm/drm_probe_helper.h>
#include <drm/drm_simple_kms_helper.h>
#include <drm/i2c/tda998x.h>

#include <media/cec-notifier.h>

#define DBG(fmt, ...)

enum {};

struct tda998x_audio_route {};

struct tda998x_audio_settings {};

struct tda998x_priv {};

#define conn_to_tda998x_priv(x)
#define enc_to_tda998x_priv(x)
#define bridge_to_tda998x_priv(x)

/* The TDA9988 series of devices use a paged register scheme.. to simplify
 * things we encode the page # in upper bits of the register #.  To read/
 * write a given register, we need to make sure CURPAGE register is set
 * appropriately.  Which implies reads/writes are not atomic.  Fun!
 */

#define REG(page, addr)
#define REG2ADDR(reg)
#define REG2PAGE(reg)

#define REG_CURPAGE


/* Page 00h: General Control */
#define REG_VERSION_LSB
#define REG_MAIN_CNTRL0
#define MAIN_CNTRL0_SR
#define MAIN_CNTRL0_DECS
#define MAIN_CNTRL0_DEHS
#define MAIN_CNTRL0_CECS
#define MAIN_CNTRL0_CEHS
#define MAIN_CNTRL0_SCALER
#define REG_VERSION_MSB
#define REG_SOFTRESET
#define SOFTRESET_AUDIO
#define SOFTRESET_I2C_MASTER
#define REG_DDC_DISABLE
#define REG_CCLK_ON
#define REG_I2C_MASTER
#define I2C_MASTER_DIS_MM
#define I2C_MASTER_DIS_FILT
#define I2C_MASTER_APP_STRT_LAT
#define REG_FEAT_POWERDOWN
#define FEAT_POWERDOWN_PREFILT
#define FEAT_POWERDOWN_CSC
#define FEAT_POWERDOWN_SPDIF
#define REG_INT_FLAGS_0
#define REG_INT_FLAGS_1
#define REG_INT_FLAGS_2
#define INT_FLAGS_2_EDID_BLK_RD
#define REG_ENA_ACLK
#define REG_ENA_VP_0
#define REG_ENA_VP_1
#define REG_ENA_VP_2
#define REG_ENA_AP
#define REG_VIP_CNTRL_0
#define VIP_CNTRL_0_MIRR_A
#define VIP_CNTRL_0_SWAP_A(x)
#define VIP_CNTRL_0_MIRR_B
#define VIP_CNTRL_0_SWAP_B(x)
#define REG_VIP_CNTRL_1
#define VIP_CNTRL_1_MIRR_C
#define VIP_CNTRL_1_SWAP_C(x)
#define VIP_CNTRL_1_MIRR_D
#define VIP_CNTRL_1_SWAP_D(x)
#define REG_VIP_CNTRL_2
#define VIP_CNTRL_2_MIRR_E
#define VIP_CNTRL_2_SWAP_E(x)
#define VIP_CNTRL_2_MIRR_F
#define VIP_CNTRL_2_SWAP_F(x)
#define REG_VIP_CNTRL_3
#define VIP_CNTRL_3_X_TGL
#define VIP_CNTRL_3_H_TGL
#define VIP_CNTRL_3_V_TGL
#define VIP_CNTRL_3_EMB
#define VIP_CNTRL_3_SYNC_DE
#define VIP_CNTRL_3_SYNC_HS
#define VIP_CNTRL_3_DE_INT
#define VIP_CNTRL_3_EDGE
#define REG_VIP_CNTRL_4
#define VIP_CNTRL_4_BLC(x)
#define VIP_CNTRL_4_BLANKIT(x)
#define VIP_CNTRL_4_CCIR656
#define VIP_CNTRL_4_656_ALT
#define VIP_CNTRL_4_TST_656
#define VIP_CNTRL_4_TST_PAT
#define REG_VIP_CNTRL_5
#define VIP_CNTRL_5_CKCASE
#define VIP_CNTRL_5_SP_CNT(x)
#define REG_MUX_AP
#define MUX_AP_SELECT_I2S
#define MUX_AP_SELECT_SPDIF
#define REG_MUX_VP_VIP_OUT
#define REG_MAT_CONTRL
#define MAT_CONTRL_MAT_SC(x)
#define MAT_CONTRL_MAT_BP
#define REG_VIDFORMAT
#define REG_REFPIX_MSB
#define REG_REFPIX_LSB
#define REG_REFLINE_MSB
#define REG_REFLINE_LSB
#define REG_NPIX_MSB
#define REG_NPIX_LSB
#define REG_NLINE_MSB
#define REG_NLINE_LSB
#define REG_VS_LINE_STRT_1_MSB
#define REG_VS_LINE_STRT_1_LSB
#define REG_VS_PIX_STRT_1_MSB
#define REG_VS_PIX_STRT_1_LSB
#define REG_VS_LINE_END_1_MSB
#define REG_VS_LINE_END_1_LSB
#define REG_VS_PIX_END_1_MSB
#define REG_VS_PIX_END_1_LSB
#define REG_VS_LINE_STRT_2_MSB
#define REG_VS_LINE_STRT_2_LSB
#define REG_VS_PIX_STRT_2_MSB
#define REG_VS_PIX_STRT_2_LSB
#define REG_VS_LINE_END_2_MSB
#define REG_VS_LINE_END_2_LSB
#define REG_VS_PIX_END_2_MSB
#define REG_VS_PIX_END_2_LSB
#define REG_HS_PIX_START_MSB
#define REG_HS_PIX_START_LSB
#define REG_HS_PIX_STOP_MSB
#define REG_HS_PIX_STOP_LSB
#define REG_VWIN_START_1_MSB
#define REG_VWIN_START_1_LSB
#define REG_VWIN_END_1_MSB
#define REG_VWIN_END_1_LSB
#define REG_VWIN_START_2_MSB
#define REG_VWIN_START_2_LSB
#define REG_VWIN_END_2_MSB
#define REG_VWIN_END_2_LSB
#define REG_DE_START_MSB
#define REG_DE_START_LSB
#define REG_DE_STOP_MSB
#define REG_DE_STOP_LSB
#define REG_TBG_CNTRL_0
#define TBG_CNTRL_0_TOP_TGL
#define TBG_CNTRL_0_TOP_SEL
#define TBG_CNTRL_0_DE_EXT
#define TBG_CNTRL_0_TOP_EXT
#define TBG_CNTRL_0_FRAME_DIS
#define TBG_CNTRL_0_SYNC_MTHD
#define TBG_CNTRL_0_SYNC_ONCE
#define REG_TBG_CNTRL_1
#define TBG_CNTRL_1_H_TGL
#define TBG_CNTRL_1_V_TGL
#define TBG_CNTRL_1_TGL_EN
#define TBG_CNTRL_1_X_EXT
#define TBG_CNTRL_1_H_EXT
#define TBG_CNTRL_1_V_EXT
#define TBG_CNTRL_1_DWIN_DIS
#define REG_ENABLE_SPACE
#define REG_HVF_CNTRL_0
#define HVF_CNTRL_0_SM
#define HVF_CNTRL_0_RWB
#define HVF_CNTRL_0_PREFIL(x)
#define HVF_CNTRL_0_INTPOL(x)
#define REG_HVF_CNTRL_1
#define HVF_CNTRL_1_FOR
#define HVF_CNTRL_1_YUVBLK
#define HVF_CNTRL_1_VQR(x)
#define HVF_CNTRL_1_PAD(x)
#define HVF_CNTRL_1_SEMI_PLANAR
#define REG_RPT_CNTRL
#define RPT_CNTRL_REPEAT(x)
#define REG_I2S_FORMAT
#define I2S_FORMAT_PHILIPS
#define I2S_FORMAT_LEFT_J
#define I2S_FORMAT_RIGHT_J
#define REG_AIP_CLKSEL
#define AIP_CLKSEL_AIP_SPDIF
#define AIP_CLKSEL_AIP_I2S
#define AIP_CLKSEL_FS_ACLK
#define AIP_CLKSEL_FS_MCLK
#define AIP_CLKSEL_FS_FS64SPDIF

/* Page 02h: PLL settings */
#define REG_PLL_SERIAL_1
#define PLL_SERIAL_1_SRL_FDN
#define PLL_SERIAL_1_SRL_IZ(x)
#define PLL_SERIAL_1_SRL_MAN_IZ
#define REG_PLL_SERIAL_2
#define PLL_SERIAL_2_SRL_NOSC(x)
#define PLL_SERIAL_2_SRL_PR(x)
#define REG_PLL_SERIAL_3
#define PLL_SERIAL_3_SRL_CCIR
#define PLL_SERIAL_3_SRL_DE
#define PLL_SERIAL_3_SRL_PXIN_SEL
#define REG_SERIALIZER
#define REG_BUFFER_OUT
#define REG_PLL_SCG1
#define REG_PLL_SCG2
#define REG_PLL_SCGN1
#define REG_PLL_SCGN2
#define REG_PLL_SCGR1
#define REG_PLL_SCGR2
#define REG_AUDIO_DIV
#define AUDIO_DIV_SERCLK_1
#define AUDIO_DIV_SERCLK_2
#define AUDIO_DIV_SERCLK_4
#define AUDIO_DIV_SERCLK_8
#define AUDIO_DIV_SERCLK_16
#define AUDIO_DIV_SERCLK_32
#define REG_SEL_CLK
#define SEL_CLK_SEL_CLK1
#define SEL_CLK_SEL_VRF_CLK(x)
#define SEL_CLK_ENA_SC_CLK
#define REG_ANA_GENERAL


/* Page 09h: EDID Control */
#define REG_EDID_DATA_0
/* next 127 successive registers are the EDID block */
#define REG_EDID_CTRL
#define REG_DDC_ADDR
#define REG_DDC_OFFS
#define REG_DDC_SEGM_ADDR
#define REG_DDC_SEGM


/* Page 10h: information frames and packets */
#define REG_IF1_HB0
#define REG_IF2_HB0
#define REG_IF3_HB0
#define REG_IF4_HB0
#define REG_IF5_HB0


/* Page 11h: audio settings and content info packets */
#define REG_AIP_CNTRL_0
#define AIP_CNTRL_0_RST_FIFO
#define AIP_CNTRL_0_SWAP
#define AIP_CNTRL_0_LAYOUT
#define AIP_CNTRL_0_ACR_MAN
#define AIP_CNTRL_0_RST_CTS
#define REG_CA_I2S
#define CA_I2S_CA_I2S(x)
#define CA_I2S_HBR_CHSTAT
#define REG_LATENCY_RD
#define REG_ACR_CTS_0
#define REG_ACR_CTS_1
#define REG_ACR_CTS_2
#define REG_ACR_N_0
#define REG_ACR_N_1
#define REG_ACR_N_2
#define REG_CTS_N
#define CTS_N_K(x)
#define CTS_N_M(x)
#define REG_ENC_CNTRL
#define ENC_CNTRL_RST_ENC
#define ENC_CNTRL_RST_SEL
#define ENC_CNTRL_CTL_CODE(x)
#define REG_DIP_FLAGS
#define DIP_FLAGS_ACR
#define DIP_FLAGS_GC
#define REG_DIP_IF_FLAGS
#define DIP_IF_FLAGS_IF1
#define DIP_IF_FLAGS_IF2
#define DIP_IF_FLAGS_IF3
#define DIP_IF_FLAGS_IF4
#define DIP_IF_FLAGS_IF5
#define REG_CH_STAT_B(x)


/* Page 12h: HDCP and OTP */
#define REG_TX3
#define REG_TX4
#define TX4_PD_RAM
#define REG_TX33
#define TX33_HDMI


/* Page 13h: Gamut related metadata packets */



/* CEC registers: (not paged)
 */
#define REG_CEC_INTSTATUS
#define CEC_INTSTATUS_CEC
#define CEC_INTSTATUS_HDMI
#define REG_CEC_CAL_XOSC_CTRL1
#define CEC_CAL_XOSC_CTRL1_ENA_CAL
#define REG_CEC_DES_FREQ2
#define CEC_DES_FREQ2_DIS_AUTOCAL
#define REG_CEC_CLK
#define CEC_CLK_FRO
#define REG_CEC_FRO_IM_CLK_CTRL
#define CEC_FRO_IM_CLK_CTRL_GHOST_DIS
#define CEC_FRO_IM_CLK_CTRL_ENA_OTP
#define CEC_FRO_IM_CLK_CTRL_IMCLK_SEL
#define CEC_FRO_IM_CLK_CTRL_FRO_DIV
#define REG_CEC_RXSHPDINTENA
#define REG_CEC_RXSHPDINT
#define CEC_RXSHPDINT_RXSENS
#define CEC_RXSHPDINT_HPD
#define REG_CEC_RXSHPDLEV
#define CEC_RXSHPDLEV_RXSENS
#define CEC_RXSHPDLEV_HPD

#define REG_CEC_ENAMODS
#define CEC_ENAMODS_EN_CEC_CLK
#define CEC_ENAMODS_DIS_FRO
#define CEC_ENAMODS_DIS_CCLK
#define CEC_ENAMODS_EN_RXSENS
#define CEC_ENAMODS_EN_HDMI
#define CEC_ENAMODS_EN_CEC


/* Device versions: */
#define TDA9989N2
#define TDA19989
#define TDA19989N2
#define TDA19988

static void
cec_write(struct tda998x_priv *priv, u16 addr, u8 val)
{}

static u8
cec_read(struct tda998x_priv *priv, u8 addr)
{}

static void cec_enamods(struct tda998x_priv *priv, u8 mods, bool enable)
{}

static void tda998x_cec_set_calibration(struct tda998x_priv *priv, bool enable)
{}

/*
 * 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)
{}

static int tda998x_cec_hook_init(void *data)
{}

static void tda998x_cec_hook_exit(void *data)
{}

static int tda998x_cec_hook_open(void *data)
{}

static void tda998x_cec_hook_release(void *data)
{}

static int
set_page(struct tda998x_priv *priv, u16 reg)
{}

static int
reg_read_range(struct tda998x_priv *priv, u16 reg, char *buf, int cnt)
{}

#define MAX_WRITE_RANGE_BUF

static void
reg_write_range(struct tda998x_priv *priv, u16 reg, u8 *p, int cnt)
{}

static int
reg_read(struct tda998x_priv *priv, u16 reg)
{}

static void
reg_write(struct tda998x_priv *priv, u16 reg, u8 val)
{}

static void
reg_write16(struct tda998x_priv *priv, u16 reg, u16 val)
{}

static void
reg_set(struct tda998x_priv *priv, u16 reg, u8 val)
{}

static void
reg_clear(struct tda998x_priv *priv, u16 reg, u8 val)
{}

static void
tda998x_reset(struct tda998x_priv *priv)
{}

/*
 * The TDA998x has a problem when trying to read the EDID close to a
 * HPD assertion: it needs a delay of 100ms to avoid timing out while
 * trying to read EDID data.
 *
 * However, tda998x_connector_get_modes() may be called at any moment
 * after tda998x_connector_detect() indicates that we are connected, so
 * we need to delay probing modes in tda998x_connector_get_modes() after
 * we have seen a HPD inactive->active transition.  This code implements
 * that delay.
 */
static void tda998x_edid_delay_done(struct timer_list *t)
{}

static void tda998x_edid_delay_start(struct tda998x_priv *priv)
{}

static int tda998x_edid_delay_wait(struct tda998x_priv *priv)
{}

/*
 * We need to run the KMS hotplug event helper outside of our threaded
 * interrupt routine as this can call back into our get_modes method,
 * which will want to make use of interrupts.
 */
static void tda998x_detect_work(struct work_struct *work)
{}

/*
 * only 2 interrupts may occur: screen plug/unplug and EDID read
 */
static irqreturn_t tda998x_irq_thread(int irq, void *data)
{}

static void
tda998x_write_if(struct tda998x_priv *priv, u8 bit, u16 addr,
		 union hdmi_infoframe *frame)
{}

static void tda998x_write_aif(struct tda998x_priv *priv,
			      const struct hdmi_audio_infoframe *cea)
{}

static void
tda998x_write_avi(struct tda998x_priv *priv, const struct drm_display_mode *mode)
{}

static void tda998x_write_vsi(struct tda998x_priv *priv,
			      const struct drm_display_mode *mode)
{}

/* Audio support */

static const struct tda998x_audio_route tda998x_audio_route[AUDIO_ROUTE_NUM] =;

/* Configure the TDA998x audio data and clock routing. */
static int tda998x_derive_routing(struct tda998x_priv *priv,
				  struct tda998x_audio_settings *s,
				  unsigned int route)
{}

/*
 * The audio clock divisor register controls a divider producing Audio_Clk_Out
 * from SERclk by dividing it by 2^n where 0 <= n <= 5.  We don't know what
 * Audio_Clk_Out or SERclk are. We guess SERclk is the same as TMDS clock.
 *
 * It seems that Audio_Clk_Out must be the smallest value that is greater
 * than 128*fs, otherwise audio does not function. There is some suggestion
 * that 126*fs is a better value.
 */
static u8 tda998x_get_adiv(struct tda998x_priv *priv, unsigned int fs)
{}

/*
 * In auto-CTS mode, the TDA998x uses a "measured time stamp" counter to
 * generate the CTS value.  It appears that the "measured time stamp" is
 * the number of TDMS clock cycles within a number of audio input clock
 * cycles defined by the k and N parameters defined below, in a similar
 * way to that which is set out in the CTS generation in the HDMI spec.
 *
 *  tmdsclk ----> mts -> /m ---> CTS
 *                 ^
 *  sclk -> /k -> /N
 *
 * CTS = mts / m, where m is 2^M.
 * /k is a divider based on the K value below, K+1 for K < 4, or 8 for K >= 4
 * /N is a divider based on the HDMI specified N value.
 *
 * This produces the following equation:
 *  CTS = tmds_clock * k * N / (sclk * m)
 *
 * When combined with the sink-side equation, and realising that sclk is
 * bclk_ratio * fs, we end up with:
 *  k = m * bclk_ratio / 128.
 *
 * Note: S/PDIF always uses a bclk_ratio of 64.
 */
static int tda998x_derive_cts_n(struct tda998x_priv *priv,
				struct tda998x_audio_settings *settings,
				unsigned int ratio)
{}

static void tda998x_audio_mute(struct tda998x_priv *priv, bool on)
{}

static void tda998x_configure_audio(struct tda998x_priv *priv)
{}

static int tda998x_audio_hw_params(struct device *dev, void *data,
				   struct hdmi_codec_daifmt *daifmt,
				   struct hdmi_codec_params *params)
{}

static void tda998x_audio_shutdown(struct device *dev, void *data)
{}

static int tda998x_audio_mute_stream(struct device *dev, void *data,
				     bool enable, int direction)
{}

static int tda998x_audio_get_eld(struct device *dev, void *data,
				 uint8_t *buf, size_t len)
{}

static const struct hdmi_codec_ops audio_codec_ops =;

static int tda998x_audio_codec_init(struct tda998x_priv *priv,
				    struct device *dev)
{}

/* DRM connector functions */

static enum drm_connector_status
tda998x_connector_detect(struct drm_connector *connector, bool force)
{}

static void tda998x_connector_destroy(struct drm_connector *connector)
{}

static const struct drm_connector_funcs tda998x_connector_funcs =;

static int read_edid_block(void *data, u8 *buf, unsigned int blk, size_t length)
{}

static int tda998x_connector_get_modes(struct drm_connector *connector)
{}

static struct drm_encoder *
tda998x_connector_best_encoder(struct drm_connector *connector)
{}

static
const struct drm_connector_helper_funcs tda998x_connector_helper_funcs =;

static int tda998x_connector_init(struct tda998x_priv *priv,
				  struct drm_device *drm)
{}

/* DRM bridge functions */

static int tda998x_bridge_attach(struct drm_bridge *bridge,
				 enum drm_bridge_attach_flags flags)
{}

static void tda998x_bridge_detach(struct drm_bridge *bridge)
{}

static enum drm_mode_status tda998x_bridge_mode_valid(struct drm_bridge *bridge,
				     const struct drm_display_info *info,
				     const struct drm_display_mode *mode)
{}

static void tda998x_bridge_enable(struct drm_bridge *bridge)
{}

static void tda998x_bridge_disable(struct drm_bridge *bridge)
{}

static void tda998x_bridge_mode_set(struct drm_bridge *bridge,
				    const struct drm_display_mode *mode,
				    const struct drm_display_mode *adjusted_mode)
{}

static const struct drm_bridge_funcs tda998x_bridge_funcs =;

/* I2C driver functions */

static int tda998x_get_audio_ports(struct tda998x_priv *priv,
				   struct device_node *np)
{}

static int tda998x_set_config(struct tda998x_priv *priv,
			      const struct tda998x_encoder_params *p)
{}

static void tda998x_destroy(struct device *dev)
{}

static int tda998x_create(struct device *dev)
{}

/* DRM encoder functions */

static int tda998x_encoder_init(struct device *dev, struct drm_device *drm)
{}

static int tda998x_bind(struct device *dev, struct device *master, void *data)
{}

static void tda998x_unbind(struct device *dev, struct device *master,
			   void *data)
{}

static const struct component_ops tda998x_ops =;

static int
tda998x_probe(struct i2c_client *client)
{}

static void tda998x_remove(struct i2c_client *client)
{}

#ifdef CONFIG_OF
static const struct of_device_id tda998x_dt_ids[] =;
MODULE_DEVICE_TABLE(of, tda998x_dt_ids);
#endif

static const struct i2c_device_id tda998x_ids[] =;
MODULE_DEVICE_TABLE(i2c, tda998x_ids);

static struct i2c_driver tda998x_driver =;

module_i2c_driver();

MODULE_AUTHOR();
MODULE_DESCRIPTION();
MODULE_LICENSE();