Message ID | 1443209368-7135-4-git-send-email-enric.balletbo@collabora.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Hi Enric, FYI, build test results on v4.3-rc2 (pls ignore if it's inappropriate base for your patch). reproduce: # apt-get install sparse make ARCH=x86_64 allmodconfig make C=1 CF=-D__CHECK_ENDIAN__ sparse warnings: (new ones prefixed by >>) >> drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c:610:33: sparse: symbol 'pxtal_data' was not declared. Should it be static? >> drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c:643:6: sparse: symbol 'sp_tx_initialization' was not declared. Should it be static? >> drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c:2188:6: sparse: symbol 'sp_initialization' was not declared. Should it be static? >> drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c:649:30: sparse: cast truncates bits from constant value (ffffff7f becomes 7f) drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c:2129:30: sparse: cast truncates bits from constant value (ffffff7f becomes 7f) >> drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c:2140:38: sparse: cast truncates bits from constant value (ffffff1f becomes 1f) Please review and possibly fold the followup patch. --- 0-DAY kernel test infrastructure Open Source Technology Center https://lists.01.org/pipermail/kbuild-all Intel Corporation
Hi Enric, On Fri, Sep 25, 2015 at 09:29:28PM +0200, Enric Balletbo i Serra wrote: > At the moment it only supports ANX7814. > > The ANX7814 is an ultra-low power Full-HD (1080p60) SlimPort transmitter > designed for portable devices. > > This driver adds initial support and supports HDMI to DP pass-through mode. > > Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com> More comments. Please look at patterns (u8->bool, use bit_ctl, etc.): I did not comment on every single instance of these. Best, > --- > drivers/gpu/drm/bridge/Kconfig | 2 + > drivers/gpu/drm/bridge/Makefile | 1 + > drivers/gpu/drm/bridge/anx78xx/Kconfig | 7 + > drivers/gpu/drm/bridge/anx78xx/Makefile | 4 + > drivers/gpu/drm/bridge/anx78xx/anx78xx.h | 41 + > drivers/gpu/drm/bridge/anx78xx/anx78xx_main.c | 228 ++ > drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c | 3148 ++++++++++++++++++++++ > drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.h | 214 ++ > drivers/gpu/drm/bridge/anx78xx/slimport_tx_reg.h | 807 ++++++ > 9 files changed, 4452 insertions(+) > create mode 100644 drivers/gpu/drm/bridge/anx78xx/Kconfig > create mode 100644 drivers/gpu/drm/bridge/anx78xx/Makefile > create mode 100644 drivers/gpu/drm/bridge/anx78xx/anx78xx.h > create mode 100644 drivers/gpu/drm/bridge/anx78xx/anx78xx_main.c > create mode 100644 drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c > create mode 100644 drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.h > create mode 100644 drivers/gpu/drm/bridge/anx78xx/slimport_tx_reg.h > > diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig > index 2de52a5..aa6fe12 100644 > --- a/drivers/gpu/drm/bridge/Kconfig > +++ b/drivers/gpu/drm/bridge/Kconfig > @@ -29,4 +29,6 @@ config DRM_PARADE_PS8622 > ---help--- > Parade eDP-LVDS bridge chip driver. > > +source "drivers/gpu/drm/bridge/anx78xx/Kconfig" > + > endmenu > diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile > index e2eef1c..e5bd38b 100644 > --- a/drivers/gpu/drm/bridge/Makefile > +++ b/drivers/gpu/drm/bridge/Makefile > @@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm > obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o > obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o > obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o > +obj-$(CONFIG_DRM_ANX78XX) += anx78xx/ > diff --git a/drivers/gpu/drm/bridge/anx78xx/Kconfig b/drivers/gpu/drm/bridge/anx78xx/Kconfig > new file mode 100644 > index 0000000..08f9c08 > --- /dev/null > +++ b/drivers/gpu/drm/bridge/anx78xx/Kconfig > @@ -0,0 +1,7 @@ > +config DRM_ANX78XX > + tristate "Analogix ANX78XX bridge" > + help > + ANX78XX is a HD video transmitter chip over micro-USB > + connector for smartphone device. > + > + > diff --git a/drivers/gpu/drm/bridge/anx78xx/Makefile b/drivers/gpu/drm/bridge/anx78xx/Makefile > new file mode 100644 > index 0000000..a843733 > --- /dev/null > +++ b/drivers/gpu/drm/bridge/anx78xx/Makefile > @@ -0,0 +1,4 @@ > +obj-${CONFIG_DRM_ANX78XX} := anx78xx.o > + > +anx78xx-y += anx78xx_main.o > +anx78xx-y += slimport_tx_drv.o > diff --git a/drivers/gpu/drm/bridge/anx78xx/anx78xx.h b/drivers/gpu/drm/bridge/anx78xx/anx78xx.h > new file mode 100644 > index 0000000..f62c8e7 > --- /dev/null > +++ b/drivers/gpu/drm/bridge/anx78xx/anx78xx.h > @@ -0,0 +1,41 @@ > +/* > + * Copyright(c) 2015, Analogix Semiconductor. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 and > + * only version 2 as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + */ > + > +#ifndef __ANX78xx_H > +#define __ANX78xx_H > + > +#include <linux/i2c.h> > +#include <linux/mutex.h> > +#include <linux/slab.h> > +#include <linux/workqueue.h> > +#include <linux/gpio/consumer.h> > + > +struct anx78xx_platform_data { > + struct gpio_desc *gpiod_pd; > + struct gpio_desc *gpiod_reset; > + spinlock_t lock; > +}; > + > +struct anx78xx { > + struct i2c_client *client; > + struct anx78xx_platform_data *pdata; > + struct delayed_work work; > + struct workqueue_struct *workqueue; > + struct mutex lock; > +}; > + > +void anx78xx_poweron(struct anx78xx *data); > +void anx78xx_poweroff(struct anx78xx *data); > + > +#endif > diff --git a/drivers/gpu/drm/bridge/anx78xx/anx78xx_main.c b/drivers/gpu/drm/bridge/anx78xx/anx78xx_main.c > new file mode 100644 > index 0000000..1e4a87e > --- /dev/null > +++ b/drivers/gpu/drm/bridge/anx78xx/anx78xx_main.c > @@ -0,0 +1,228 @@ > +/* > + * Copyright(c) 2015, Analogix Semiconductor. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 and > + * only version 2 as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + */ > +#include <linux/delay.h> > +#include <linux/interrupt.h> > +#include <linux/kernel.h> > +#include <linux/module.h> > +#include <linux/types.h> > +#include <linux/err.h> > +#include <linux/async.h> > +#include <linux/of_gpio.h> > +#include <linux/of_platform.h> > +#include <linux/delay.h> > + > +#include "anx78xx.h" > +#include "slimport_tx_drv.h" > + > +void anx78xx_poweron(struct anx78xx *anx78xx) > +{ > + struct anx78xx_platform_data *pdata = anx78xx->pdata; > + > + gpiod_set_value_cansleep(pdata->gpiod_reset, 0); > + usleep_range(1000, 2000); > + > + gpiod_set_value_cansleep(pdata->gpiod_pd, 0); > + usleep_range(1000, 2000); > + > + gpiod_set_value_cansleep(pdata->gpiod_reset, 1); > +} > + > +void anx78xx_poweroff(struct anx78xx *anx78xx) > +{ > + struct anx78xx_platform_data *pdata = anx78xx->pdata; > + > + gpiod_set_value_cansleep(pdata->gpiod_reset, 0); > + usleep_range(1000, 2000); > + > + gpiod_set_value_cansleep(pdata->gpiod_pd, 1); > + usleep_range(1000, 2000); > +} > + > +static int anx78xx_init_gpio(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + struct anx78xx_platform_data *pdata = anx78xx->pdata; > + > + /* gpio for chip power down */ > + pdata->gpiod_pd = devm_gpiod_get(dev, "pd", GPIOD_OUT_HIGH); > + if (IS_ERR(pdata->gpiod_pd)) { > + dev_err(dev, "unable to claim pd gpio\n"); > + return PTR_ERR(pdata->gpiod_pd); > + } > + > + /* gpio for chip reset */ > + pdata->gpiod_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); > + if (IS_ERR(pdata->gpiod_reset)) { > + dev_err(dev, "unable to claim reset gpio\n"); > + return PTR_ERR(pdata->gpiod_reset); > + } > + > + return 0; > +} > + > +static int anx78xx_system_init(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + > + if (!sp_chip_detect(anx78xx)) { > + anx78xx_poweroff(anx78xx); > + dev_err(dev, "failed to detect anx78xx\n"); > + return -ENODEV; > + } > + > + sp_tx_variable_init(); > + return 0; > +} I think this function might be better off as "sp_system_init" in tx_drv.c, since sp_chip_detect and sp_tx_variable_init are only called from here anyway. > + > +static void anx78xx_work_func(struct work_struct *work) > +{ > + struct anx78xx *anx78xx = container_of(work, struct anx78xx, > + work.work); > + int workqueue_timer = 0; > + > + if (sp_tx_current_state() >= STATE_PLAY_BACK) > + workqueue_timer = 500; > + else > + workqueue_timer = 100; So whenever the bridge is not outputing anything, the workqueue will be exectuted every 100ms? That seems like a lot... > + mutex_lock(&anx78xx->lock); > + sp_main_process(anx78xx); > + mutex_unlock(&anx78xx->lock); > + queue_delayed_work(anx78xx->workqueue, &anx78xx->work, > + msecs_to_jiffies(workqueue_timer)); > +} > + > +static int anx78xx_i2c_probe(struct i2c_client *client, > + const struct i2c_device_id *id) > +{ > + struct anx78xx *anx78xx; > + int ret; > + > + if (!i2c_check_functionality(client->adapter, > + I2C_FUNC_SMBUS_I2C_BLOCK)) { > + dev_err(&client->dev, "i2c bus does not support the device\n"); > + return -ENODEV; > + } > + > + anx78xx = devm_kzalloc(&client->dev, sizeof(*anx78xx), GFP_KERNEL); > + if (!anx78xx) > + return -ENOMEM; > + > + anx78xx->pdata = devm_kzalloc(&client->dev, > + sizeof(struct anx78xx_platform_data), > + GFP_KERNEL); > + if (!anx78xx->pdata) > + return -ENOMEM; > + > + anx78xx->client = client; > + > + i2c_set_clientdata(client, anx78xx); > + > + mutex_init(&anx78xx->lock); > + > + ret = anx78xx_init_gpio(anx78xx); > + if (ret) { > + dev_err(&client->dev, "failed to initialize gpios\n"); > + return ret; > + } > + > + INIT_DELAYED_WORK(&anx78xx->work, anx78xx_work_func); > + > + anx78xx->workqueue = create_singlethread_workqueue("anx78xx_work"); > + if (!anx78xx->workqueue) { > + dev_err(&client->dev, "failed to create work queue\n"); > + return -ENOMEM; > + } > + > + ret = anx78xx_system_init(anx78xx); > + if (ret) { > + dev_err(&client->dev, "failed to initialize anx78xx\n"); > + goto cleanup; > + } > + > + /* enable driver */ > + queue_delayed_work(anx78xx->workqueue, &anx78xx->work, 0); > + > + return 0; > + > +cleanup: > + destroy_workqueue(anx78xx->workqueue); > + return ret; > +} > + > +static int anx78xx_i2c_remove(struct i2c_client *client) > +{ > + struct anx78xx *anx78xx = i2c_get_clientdata(client); > + > + destroy_workqueue(anx78xx->workqueue); > + > + return 0; > +} > + > +static int anx78xx_i2c_suspend(struct device *dev) > +{ > + struct i2c_client *client = container_of(dev, struct i2c_client, dev); > + struct anx78xx *anx78xx = i2c_get_clientdata(client); > + > + cancel_delayed_work_sync(&anx78xx->work); > + flush_workqueue(anx78xx->workqueue); > + anx78xx_poweroff(anx78xx); > + sp_tx_clean_state_machine(); > + > + return 0; > +} > + > +static int anx78xx_i2c_resume(struct device *dev) > +{ > + struct i2c_client *client = container_of(dev, struct i2c_client, dev); > + struct anx78xx *anx78xx = i2c_get_clientdata(client); > + > + queue_delayed_work(anx78xx->workqueue, &anx78xx->work, 0); > + > + return 0; > +} > + > +static SIMPLE_DEV_PM_OPS(anx78xx_i2c_pm_ops, > + anx78xx_i2c_suspend, anx78xx_i2c_resume); > + > +static const struct i2c_device_id anx78xx_id[] = { > + {"anx7814", 0}, > + { /* sentinel */ } > +}; > + > +MODULE_DEVICE_TABLE(i2c, anx78xx_id); > + > +static const struct of_device_id anx78xx_match_table[] = { > + {.compatible = "analogix,anx7814",}, > + { /* sentinel */ }, > +}; > + > +MODULE_DEVICE_TABLE(of, anx78xx_match_table); This should be protected by CONFIG_OF. > + > +static struct i2c_driver anx78xx_driver = { > + .driver = { > + .name = "anx7814", > + .pm = &anx78xx_i2c_pm_ops, > + .of_match_table = of_match_ptr(anx78xx_match_table), > + }, > + .probe = anx78xx_i2c_probe, > + .remove = anx78xx_i2c_remove, > + .id_table = anx78xx_id, > +}; > + > +module_i2c_driver(anx78xx_driver); > + > +MODULE_DESCRIPTION("Slimport transmitter ANX78XX driver"); > +MODULE_AUTHOR("Junhua Xia <jxia@analogixsemi.com>"); > +MODULE_LICENSE("GPL v2"); > +MODULE_VERSION("1.1"); > diff --git a/drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c b/drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c > new file mode 100644 > index 0000000..7721326 > --- /dev/null > +++ b/drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c > @@ -0,0 +1,3148 @@ > +/* > + * Copyright(c) 2015, Analogix Semiconductor. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 and > + * only version 2 as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + */ > +#include <linux/delay.h> > +#include <linux/types.h> > + > +#include "anx78xx.h" > +#include "slimport_tx_drv.h" > + > +struct slimport { > + int block_en; /* HDCP control enable/ disable from AP */ > + > + u8 tx_test_bw; > + bool tx_test_lt; > + bool tx_test_edid; I think these variables, and the ones below, could do we more descriptive, or at least more systematic names. E.g. the last 2 tx_test variables above are bool, but the first one is u8, this is a little confusing. > + u8 changed_bandwidth; > + > + u8 hdmi_dvi_status; > + u8 need_clean_status; bool > + > + u8 ds_vid_stb_cntr; Only set to 0, once, then never used. > + u8 hdcp_fail_count; > + > + u8 edid_break; > + u8 edid_checksum; > + u8 edid_blocks[256]; This variable is only used in sp_edid_process: allocate the buffer there. > + > + u8 read_edid_flag; > + > + u8 down_sample_en; > + > + struct packet_avi tx_packet_avi; > + struct packet_spd tx_packet_spd; > + struct packet_mpeg tx_packet_mpeg; > + struct audio_info_frame tx_audioinfoframe; > + > + struct common_int common_int_status; > + struct hdmi_rx_int hdmi_rx_int_status; > + > + enum sp_tx_state tx_system_state; > + enum sp_tx_state tx_system_state_bak; > + enum audio_output_status tx_ao_state; > + enum video_output_status tx_vo_state; > + enum sink_connection_status tx_sc_state; > + enum sp_tx_lt_status tx_lt_state; > + enum hdcp_status hcdp_state; > +}; > + > +static struct slimport sp; > + > +static const u16 chipid_list[] = { > + 0x7818, > + 0x7816, > + 0x7814, > + 0x7812, > + 0x7810, > + 0x7806, > + 0x7802 Add a comma after the last entry, and reverse the order. > +}; > + > +static void sp_hdmi_rx_new_vsi_int(struct anx78xx *anx78xx); > +static u8 sp_hdcp_cap_check(struct anx78xx *anx78xx); > +static void sp_tx_show_information(struct anx78xx *anx78xx); > +static void sp_print_system_state(struct anx78xx *anx78xx, u8 ss); > + > +static int sp_read_reg(struct anx78xx *anx78xx, u8 slave_addr, > + u8 offset, u8 *buf) > +{ > + int ret; > + struct i2c_client *client = anx78xx->client; > + > + client->addr = slave_addr >> 1; > + > + ret = i2c_smbus_read_byte_data(client, offset); > + if (ret < 0) { > + dev_err(&client->dev, "failed to read i2c addr=%x\n", > + slave_addr); > + return ret; > + } > + > + *buf = ret; > + > + return 0; > +} This function, and write_reg below, returns error values, but those are ignored by all callers... I think callers should propagate errors. > + > +static int sp_write_reg(struct anx78xx *anx78xx, u8 slave_addr, > + u8 offset, u8 value) > +{ > + int ret; > + struct i2c_client *client = anx78xx->client; > + > + client->addr = slave_addr >> 1; > + > + ret = i2c_smbus_write_byte_data(client, offset, value); > + if (ret < 0) > + dev_err(&client->dev, "failed to write i2c addr=%x\n", > + slave_addr); > + > + return ret; > +} > + > +static u8 sp_i2c_read_byte(struct anx78xx *anx78xx, > + u8 dev, u8 offset) > +{ > + u8 ret; > + > + sp_read_reg(anx78xx, dev, offset, &ret); > + return ret; > +} I don't think this function should exist: it is a simple wrapper that ignores errors (and returns random values on error). > + > +static void sp_reg_bit_ctl(struct anx78xx *anx78xx, u8 addr, u8 offset, > + u8 data, bool enable) > +{ > + u8 regval; > + > + sp_read_reg(anx78xx, addr, offset, ®val); > + if (enable) { > + if ((regval & data) != data) { > + regval |= data; > + sp_write_reg(anx78xx, addr, offset, regval); > + } > + } else { > + if ((regval & data) == data) { > + regval &= ~data; > + sp_write_reg(anx78xx, addr, offset, regval); > + } > + } > +} > + > +static inline void sp_write_reg_or(struct anx78xx *anx78xx, u8 address, > + u8 offset, u8 mask) > +{ > + sp_write_reg(anx78xx, address, offset, > + sp_i2c_read_byte(anx78xx, address, offset) | mask); > +} > + > +static inline void sp_write_reg_and(struct anx78xx *anx78xx, u8 address, > + u8 offset, u8 mask) > +{ > + sp_write_reg(anx78xx, address, offset, > + sp_i2c_read_byte(anx78xx, address, offset) & mask); > +} > + > +static inline void sp_write_reg_and_or(struct anx78xx *anx78xx, u8 address, > + u8 offset, u8 and_mask, u8 or_mask) > +{ > + sp_write_reg(anx78xx, address, offset, > + (sp_i2c_read_byte(anx78xx, address, offset) & and_mask) > + | or_mask); > +} > + > +static inline void sp_write_reg_or_and(struct anx78xx *anx78xx, u8 address, > + u8 offset, u8 or_mask, u8 and_mask) > +{ > + sp_write_reg(anx78xx, address, offset, > + (sp_i2c_read_byte(anx78xx, address, offset) | or_mask) > + & and_mask); > +} > + > +static inline void sp_tx_video_mute(struct anx78xx *anx78xx, bool enable) > +{ > + sp_reg_bit_ctl(anx78xx, TX_P2, VID_CTRL1, VIDEO_MUTE, enable); > +} > + > +static inline void hdmi_rx_mute_audio(struct anx78xx *anx78xx, bool enable) > +{ > + sp_reg_bit_ctl(anx78xx, RX_P0, RX_MUTE_CTRL, AUD_MUTE, enable); > +} > + > +static inline void hdmi_rx_mute_video(struct anx78xx *anx78xx, bool enable) > +{ > + sp_reg_bit_ctl(anx78xx, RX_P0, RX_MUTE_CTRL, VID_MUTE, enable); > +} > + > +static inline void sp_tx_addronly_set(struct anx78xx *anx78xx, bool enable) > +{ > + sp_reg_bit_ctl(anx78xx, TX_P0, AUX_CTRL2, ADDR_ONLY_BIT, enable); > +} > + > +static inline void sp_tx_set_link_bw(struct anx78xx *anx78xx, u8 bw) > +{ > + sp_write_reg(anx78xx, TX_P0, SP_TX_LINK_BW_SET_REG, bw); > +} > + > +static inline u8 sp_tx_get_link_bw(struct anx78xx *anx78xx) > +{ > + return (sp_i2c_read_byte(anx78xx, TX_P0, SP_TX_LINK_BW_SET_REG) & > + LINK_BW_SET_MASK); > +} > + > +static inline bool sp_tx_get_pll_lock_status(struct anx78xx *anx78xx) > +{ > + u8 byte; > + > + byte = sp_i2c_read_byte(anx78xx, TX_P0, TX_DEBUG1); > + > + return (byte & DEBUG_PLL_LOCK) != 0; > +} > + > +static inline void gen_m_clk_with_downspeading(struct anx78xx *anx78xx) downspeeding? downspreading? > +{ > + sp_write_reg_or(anx78xx, TX_P0, SP_TX_M_CALCU_CTRL, M_GEN_CLK_SEL); > +} > + > +static inline void gen_m_clk_without_downspeading(struct anx78xx *anx78xx) > +{ > + sp_write_reg_and(anx78xx, TX_P0, SP_TX_M_CALCU_CTRL, (~M_GEN_CLK_SEL)); > +} Can't you use bit_ctl for these 2? > + > +static inline void hdmi_rx_set_hpd(struct anx78xx *anx78xx, bool enable) > +{ > + if (enable) > + sp_write_reg_or(anx78xx, TX_P2, SP_TX_VID_CTRL3_REG, HPD_OUT); > + else > + sp_write_reg_and(anx78xx, TX_P2, SP_TX_VID_CTRL3_REG, > + ~HPD_OUT); > +} > + > +static inline void hdmi_rx_set_termination(struct anx78xx *anx78xx, > + bool enable) > +{ > + if (enable) > + sp_write_reg_and(anx78xx, RX_P0, HDMI_RX_TMDS_CTRL_REG7, > + ~TERM_PD); > + else > + sp_write_reg_or(anx78xx, RX_P0, HDMI_RX_TMDS_CTRL_REG7, > + TERM_PD); > +} bit_ctl for these 2 as well, and please check the rest of the code too. > + > +static inline void sp_tx_clean_hdcp_status(struct anx78xx *anx78xx) > +{ > + sp_write_reg(anx78xx, TX_P0, TX_HDCP_CTRL0, 0x03); > + sp_write_reg_or(anx78xx, TX_P0, TX_HDCP_CTRL0, RE_AUTH); > + usleep_range(2000, 4000); > +} > + > +static inline void sp_tx_link_phy_initialization(struct anx78xx *anx78xx) Please do not force inline, especially for these long-ish functions, the compiler will do the right thing. > +{ > + sp_write_reg(anx78xx, TX_P2, SP_TX_ANALOG_CTRL0, 0x02); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG0, 0x01); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG10, 0x00); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG1, 0x03); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG11, 0x00); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG2, 0x07); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG12, 0x00); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG3, 0x7f); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG13, 0x00); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG4, 0x71); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG14, 0x0c); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG5, 0x6b); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG15, 0x42); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG6, 0x7f); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG16, 0x1e); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG7, 0x73); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG17, 0x3e); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG8, 0x7f); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG18, 0x72); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG9, 0x7f); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG19, 0x7e); Weird order, any reason not to write those from 1 to 19? > +} > + > +static inline void sp_tx_set_sys_state(struct anx78xx *anx78xx, u8 ss) > +{ > + struct device *dev = &anx78xx->client->dev; > + > + dev_dbg(dev, "set: clean_status: %x,\n", sp.need_clean_status); > + > + if ((sp.tx_system_state >= STATE_LINK_TRAINING) && > + (ss < STATE_LINK_TRAINING)) > + sp_write_reg_or(anx78xx, TX_P0, SP_TX_ANALOG_PD_REG, CH0_PD); > + > + sp.tx_system_state_bak = sp.tx_system_state; > + sp.tx_system_state = ss; > + sp.need_clean_status = 1; > + sp_print_system_state(anx78xx, sp.tx_system_state); > +} > + > +static inline void reg_hardware_reset(struct anx78xx *anx78xx) > +{ > + sp_write_reg_or(anx78xx, TX_P2, SP_TX_RST_CTRL_REG, HW_RST); > + sp_tx_clean_state_machine(); > + sp_tx_set_sys_state(anx78xx, STATE_SP_INITIALIZED); > + msleep(500); > +} > + > +static inline void write_dpcd_addr(struct anx78xx *anx78xx, u8 addrh, > + u8 addrm, u8 addrl) > +{ > + u8 regval; > + > + if (sp_i2c_read_byte(anx78xx, TX_P0, AUX_ADDR_7_0) != addrl) > + sp_write_reg(anx78xx, TX_P0, AUX_ADDR_7_0, addrl); > + > + if (sp_i2c_read_byte(anx78xx, TX_P0, AUX_ADDR_15_8) != addrm) > + sp_write_reg(anx78xx, TX_P0, AUX_ADDR_15_8, addrm); > + > + sp_read_reg(anx78xx, TX_P0, AUX_ADDR_19_16, ®val); This is confusing, both sp_i2c_read_byte and sp_read_reg are used in the same function. Pick one. > + > + if ((regval & 0x0f) != (addrh & 0x0f)) > + sp_write_reg(anx78xx, TX_P0, AUX_ADDR_19_16, > + (regval & 0xf0) | addrh); 1 space before & > +} > + > +static inline void goto_next_system_state(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + > + dev_dbg(dev, "next: clean_status: %x,\n", sp.need_clean_status); > + > + sp.tx_system_state_bak = sp.tx_system_state; > + sp.tx_system_state++; > + sp_print_system_state(anx78xx, sp.tx_system_state); > +} > + > +static inline void redo_cur_system_state(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + > + dev_dbg(dev, "redo: clean_status: %x,\n", sp.need_clean_status); > + > + sp.need_clean_status = 1; > + sp.tx_system_state_bak = sp.tx_system_state; > + sp_print_system_state(anx78xx, sp.tx_system_state); > +} > + > +static inline void system_state_change_with_case(struct anx78xx *anx78xx, > + u8 status) > +{ > + struct device *dev = &anx78xx->client->dev; > + > + if (sp.tx_system_state < status) > + return; > + > + dev_dbg(dev, "change_case: clean_status: %xm,\n", > + sp.need_clean_status); > + > + if (sp.tx_system_state >= STATE_LINK_TRAINING && > + status < STATE_LINK_TRAINING) > + sp_write_reg_or(anx78xx, TX_P0, SP_TX_ANALOG_PD_REG, > + CH0_PD); > + > + sp.need_clean_status = 1; > + sp.tx_system_state_bak = sp.tx_system_state; > + sp.tx_system_state = status; > + sp_print_system_state(anx78xx, sp.tx_system_state); > +} > + > +static void sp_wait_aux_op_finish(struct anx78xx *anx78xx, u8 *err_flag) Return an int and use that as error flag. > +{ > + u8 cnt; > + u8 regval; > + struct device *dev = &anx78xx->client->dev; > + > + *err_flag = 0; > + cnt = 150; > + while (sp_i2c_read_byte(anx78xx, TX_P0, AUX_CTRL2) & AUX_OP_EN) { > + usleep_range(2000, 4000); > + if (cnt-- == 0) { > + dev_err(dev, "aux operate failed!\n"); > + *err_flag = 1; > + break; > + } > + } > + > + sp_read_reg(anx78xx, TX_P0, SP_TX_AUX_STATUS, ®val); > + if (regval & 0x0f) { > + dev_err(dev, "wait aux operation status %.2x\n", regval); > + *err_flag = 1; > + } > +} > + > +static void sp_print_system_state(struct anx78xx *anx78xx, u8 ss) > +{ > + struct device *dev = &anx78xx->client->dev; > + > + switch (ss) { > + case STATE_WAITTING_CABLE_PLUG: > + dev_dbg(dev, "-STATE_WAITTING_CABLE_PLUG-\n"); > + break; > + case STATE_SP_INITIALIZED: > + dev_dbg(dev, "-STATE_SP_INITIALIZED-\n"); > + break; > + case STATE_SINK_CONNECTION: > + dev_dbg(dev, "-STATE_SINK_CONNECTION-\n"); > + break; > + case STATE_PARSE_EDID: > + dev_dbg(dev, "-STATE_PARSE_EDID-\n"); > + break; > + case STATE_LINK_TRAINING: > + dev_dbg(dev, "-STATE_LINK_TRAINING-\n"); > + break; > + case STATE_VIDEO_OUTPUT: > + dev_dbg(dev, "-STATE_VIDEO_OUTPUT-\n"); > + break; > + case STATE_HDCP_AUTH: > + dev_dbg(dev, "-STATE_HDCP_AUTH-\n"); > + break; > + case STATE_AUDIO_OUTPUT: > + dev_dbg(dev, "-STATE_AUDIO_OUTPUT-\n"); > + break; > + case STATE_PLAY_BACK: > + dev_dbg(dev, "-STATE_PLAY_BACK-\n"); > + break; > + default: > + dev_err(dev, "unknown system state\n"); > + break; > + } > +} > + > +static void sp_tx_rst_aux(struct anx78xx *anx78xx) > +{ > + sp_write_reg_or(anx78xx, TX_P2, RST_CTRL2, AUX_RST); > + sp_write_reg_and(anx78xx, TX_P2, RST_CTRL2, ~AUX_RST); bit_ctl? > +} > + > +static u8 sp_tx_aux_dpcdread_bytes(struct anx78xx *anx78xx, u8 addrh, > + u8 addrm, u8 addrl, u8 ccount, u8 *pbuf) > +{ > + u8 regval, regval1, i; > + u8 bok; > + struct device *dev = &anx78xx->client->dev; > + > + sp_write_reg(anx78xx, TX_P0, BUF_DATA_COUNT, 0x80); > + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, ((ccount - 1) << 4) | 0x09); > + write_dpcd_addr(anx78xx, addrh, addrm, addrl); > + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, AUX_OP_EN); > + usleep_range(2000, 4000); > + > + sp_wait_aux_op_finish(anx78xx, &bok); > + if (bok) { > + dev_err(dev, "aux read failed\n"); > + sp_read_reg(anx78xx, TX_P2, SP_TX_INT_STATUS1, ®val); > + sp_read_reg(anx78xx, TX_P0, TX_DEBUG1, ®val1); > + if (!(regval1 & POLLING_EN) || (regval & POLLING_ERR)) > + sp_tx_rst_aux(anx78xx); > + return 1; > + } > + > + for (i = 0; i < ccount; i++) { > + sp_read_reg(anx78xx, TX_P0, BUF_DATA_0 + i, ®val); > + *(pbuf + i) = regval; > + if (i >= MAX_BUF_CNT) > + break; > + } > + return 0; > +} > + > +static u8 sp_tx_aux_dpcdwrite_bytes(struct anx78xx *anx78xx, u8 addrh, > + u8 addrm, u8 addrl, u8 ccount, u8 *pbuf) > +{ > + u8 regval, i, ret; > + > + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, ((ccount - 1) << 4) | 0x08); > + write_dpcd_addr(anx78xx, addrh, addrm, addrl); > + for (i = 0; i < ccount; i++) { && i < 16 > + regval = *pbuf; > + pbuf++; > + sp_write_reg(anx78xx, TX_P0, BUF_DATA_0 + i, regval); , pbuf[i]); and drop regval. > + > + if (i >= 15) > + break; > + } > + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, AUX_OP_EN); > + sp_wait_aux_op_finish(anx78xx, &ret); > + return ret; > +} > + > +static u8 sp_tx_aux_dpcdwrite_byte(struct anx78xx *anx78xx, u8 addrh, > + u8 addrm, u8 addrl, u8 data1) > +{ > + u8 ret; > + > + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, 0x08); > + write_dpcd_addr(anx78xx, addrh, addrm, addrl); > + sp_write_reg(anx78xx, TX_P0, BUF_DATA_0, data1); > + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, AUX_OP_EN); > + sp_wait_aux_op_finish(anx78xx, &ret); > + return ret; > +} > + > +static void sp_block_power_ctrl(struct anx78xx *anx78xx, > + enum sp_tx_power_block sp_tx_pd_block, > + u8 power) AFAICS, power is only ON or OFF. Use a bool. > +{ > + struct device *dev = &anx78xx->client->dev; > + > + if (power == SP_POWER_ON) > + sp_write_reg_and(anx78xx, TX_P2, SP_POWERD_CTRL_REG, > + ~sp_tx_pd_block); > + else > + sp_write_reg_or(anx78xx, TX_P2, SP_POWERD_CTRL_REG, > + sp_tx_pd_block); Again, would be simple with bit_ctl. > + > + dev_dbg(dev, "sp_tx_power_on: %.2x\n", sp_tx_pd_block); > +} > + > +static void sp_vbus_power_off(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + int i; > + > + for (i = 0; i < 5; i++) { > + sp_write_reg_and(anx78xx, TX_P2, TX_PLL_FILTER5, > + ~P5V_PROTECT_PD & ~SHORT_PROTECT_PD); > + sp_write_reg_or(anx78xx, TX_P2, TX_PLL_FILTER, V33_SWITCH_ON); > + if (!(sp_i2c_read_byte(anx78xx, TX_P2, TX_PLL_FILTER5) > + & 0xc0)) { > + dev_dbg(dev, "3.3V output enabled\n"); > + break; > + } > + } > +} > + > +void sp_tx_clean_state_machine(void) > +{ > + sp.tx_system_state = STATE_WAITTING_CABLE_PLUG; > + sp.tx_system_state_bak = STATE_WAITTING_CABLE_PLUG; > + sp.tx_sc_state = SC_INIT; > + sp.tx_lt_state = LT_INIT; > + sp.hcdp_state = HDCP_CAPABLE_CHECK; > + sp.tx_vo_state = VO_WAIT_VIDEO_STABLE; > + sp.tx_ao_state = AO_INIT; > +} > + > +enum sp_tx_state sp_tx_current_state(void) > +{ > + return sp.tx_system_state; > +} > + > +void sp_tx_variable_init(void) > +{ > + sp.block_en = 1; > + > + sp.tx_system_state = STATE_WAITTING_CABLE_PLUG; > + sp.tx_system_state_bak = STATE_WAITTING_CABLE_PLUG; > + > + sp.edid_break = 0; > + sp.read_edid_flag = 0; > + sp.edid_checksum = 0; > + > + memset(sp.edid_blocks, 0, 256); > + > + sp.tx_lt_state = LT_INIT; > + sp.hcdp_state = HDCP_CAPABLE_CHECK; > + sp.need_clean_status = 0; > + sp.tx_sc_state = SC_INIT; > + sp.tx_vo_state = VO_WAIT_VIDEO_STABLE; > + sp.tx_ao_state = AO_INIT; > + sp.changed_bandwidth = LINK_5P4G; > + sp.hdmi_dvi_status = HDMI_MODE; > + > + sp.tx_test_lt = 0; > + sp.tx_test_bw = 0; > + sp.tx_test_edid = 0; > + > + sp.ds_vid_stb_cntr = 0; > + sp.hdcp_fail_count = 0; > +} > + > +static void hdmi_rx_tmds_phy_initialization(struct anx78xx *anx78xx) > +{ > + sp_write_reg(anx78xx, RX_P0, HDMI_RX_TMDS_CTRL_REG2, 0xa9); > + sp_write_reg(anx78xx, RX_P0, HDMI_RX_TMDS_CTRL_REG7, 0x80); > + > + sp_write_reg(anx78xx, RX_P0, HDMI_RX_TMDS_CTRL_REG1, 0x90); > + sp_write_reg(anx78xx, RX_P0, HDMI_RX_TMDS_CTRL_REG6, 0x92); > + sp_write_reg(anx78xx, RX_P0, HDMI_RX_TMDS_CTRL_REG20, 0xf2); > +} > + > +static void hdmi_rx_initialization(struct anx78xx *anx78xx) > +{ > + sp_write_reg(anx78xx, RX_P0, RX_MUTE_CTRL, AUD_MUTE | VID_MUTE); > + sp_write_reg_or(anx78xx, RX_P0, RX_CHIP_CTRL, > + MAN_HDMI5V_DET | PLLLOCK_CKDT_EN | DIGITAL_CKDT_EN); > + > + sp_write_reg_or(anx78xx, RX_P0, RX_SRST, HDCP_MAN_RST | SW_MAN_RST | > + TMDS_RST | VIDEO_RST); > + sp_write_reg_and(anx78xx, RX_P0, RX_SRST, ~HDCP_MAN_RST & > + ~SW_MAN_RST & ~TMDS_RST & ~VIDEO_RST); > + > + sp_write_reg_or(anx78xx, RX_P0, RX_AEC_EN0, AEC_EN06 | AEC_EN05); > + sp_write_reg_or(anx78xx, RX_P0, RX_AEC_EN2, AEC_EN21); > + sp_write_reg_or(anx78xx, RX_P0, RX_AEC_CTRL, AVC_EN | AAC_OE | AAC_EN); > + > + sp_write_reg_and(anx78xx, RX_P0, RX_SYS_PWDN1, ~PWDN_CTRL); > + > + sp_write_reg_or(anx78xx, RX_P0, RX_VID_DATA_RNG, R2Y_INPUT_LIMIT); > + sp_write_reg(anx78xx, RX_P0, 0x65, 0xc4); > + sp_write_reg(anx78xx, RX_P0, 0x66, 0x18); > + > + /* enable DDC stretch */ > + sp_write_reg(anx78xx, TX_P0, TX_EXTRA_ADDR, 0x50); > + > + hdmi_rx_tmds_phy_initialization(anx78xx); > + hdmi_rx_set_hpd(anx78xx, 0); > + hdmi_rx_set_termination(anx78xx, 0); > +} > + > +struct anx78xx_clock_data const pxtal_data[XTAL_CLK_NUM] = { > + {19, 192}, > + {24, 240}, > + {25, 250}, > + {26, 260}, > + {27, 270}, > + {38, 384}, > + {52, 520}, > + {27, 270}, > +}; > + > +static void xtal_clk_sel(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + > + dev_dbg(dev, "define XTAL_CLK: %x\n", XTAL_27M); > + sp_write_reg_and_or(anx78xx, TX_P2, > + TX_ANALOG_DEBUG2, ~0x3c, 0x3c & (XTAL_27M << 2)); > + sp_write_reg(anx78xx, TX_P0, 0xec, pxtal_data[XTAL_27M].xtal_clk_m10); > + sp_write_reg(anx78xx, TX_P0, 0xed, 0xec, 0xed, etc: Please define these magic numbers. > + ((pxtal_data[XTAL_27M].xtal_clk_m10 & 0xff00) >> 2) > + | pxtal_data[XTAL_27M].xtal_clk); > + > + sp_write_reg(anx78xx, TX_P0, I2C_GEN_10US_TIMER0, > + pxtal_data[XTAL_27M].xtal_clk_m10); > + sp_write_reg(anx78xx, TX_P0, I2C_GEN_10US_TIMER1, > + (pxtal_data[XTAL_27M].xtal_clk_m10 & 0xff00) >> 8); > + sp_write_reg(anx78xx, TX_P0, 0xbf, pxtal_data[XTAL_27M].xtal_clk - 1); > + > + sp_write_reg_and_or(anx78xx, RX_P0, 0x49, 0x07, > + ((pxtal_data[XTAL_27M].xtal_clk >> 1) - 2) << 3); > +} > + > +void sp_tx_initialization(struct anx78xx *anx78xx) > +{ > + sp_write_reg(anx78xx, TX_P0, AUX_CTRL2, 0x30); > + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, 0x08); > + > + sp_write_reg_and(anx78xx, TX_P0, TX_HDCP_CTRL, > + (u8)~AUTO_EN & ~AUTO_START); > + sp_write_reg(anx78xx, TX_P0, OTP_KEY_PROTECT1, OTP_PSW1); > + sp_write_reg(anx78xx, TX_P0, OTP_KEY_PROTECT2, OTP_PSW2); > + sp_write_reg(anx78xx, TX_P0, OTP_KEY_PROTECT3, OTP_PSW3); > + sp_write_reg_or(anx78xx, TX_P0, HDCP_KEY_CMD, DISABLE_SYNC_HDCP); > + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL8_REG, VID_VRES_TH); > + > + sp_write_reg(anx78xx, TX_P0, HDCP_AUTO_TIMER, HDCP_AUTO_TIMER_VAL); > + sp_write_reg_or(anx78xx, TX_P0, TX_HDCP_CTRL, LINK_POLLING); > + > + sp_write_reg_or(anx78xx, TX_P0, TX_LINK_DEBUG, M_VID_DEBUG); > + sp_write_reg_or(anx78xx, TX_P2, TX_ANALOG_DEBUG2, POWERON_TIME_1P5MS); > + > + xtal_clk_sel(anx78xx); > + sp_write_reg(anx78xx, TX_P0, AUX_DEFER_CTRL, 0x8c); > + > + sp_write_reg_or(anx78xx, TX_P0, TX_DP_POLLING, AUTO_POLLING_DISABLE); > + /* > + * Short the link intergrity check timer to speed up bstatus > + * polling for HDCP CTS item 1A-07 > + */ > + sp_write_reg(anx78xx, TX_P0, SP_TX_LINK_CHK_TIMER, 0x1d); > + sp_write_reg_or(anx78xx, TX_P0, TX_MISC, EQ_TRAINING_LOOP); > + > + sp_write_reg_or(anx78xx, TX_P0, SP_TX_ANALOG_PD_REG, CH0_PD); > + > + sp_write_reg(anx78xx, TX_P2, SP_TX_INT_CTRL_REG, 0x01); > + /* disable HDCP mismatch function for VGA dongle */ > + sp_tx_link_phy_initialization(anx78xx); > + gen_m_clk_with_downspeading(anx78xx); > + > + sp.down_sample_en = 0; > +} > + > +bool sp_chip_detect(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + u16 id; > + u8 idh = 0, idl = 0; > + int i; > + > + anx78xx_poweron(anx78xx); > + > + /* check chip id */ > + sp_read_reg(anx78xx, TX_P2, SP_TX_DEV_IDL_REG, &idl); > + sp_read_reg(anx78xx, TX_P2, SP_TX_DEV_IDH_REG, &idh); > + id = idl | (idh << 8); > + > + dev_dbg(dev, "CHIPID: ANX%x\n", id & 0xffff); > + > + for (i = 0; i < ARRAY_SIZE(chipid_list); i++) { > + if (id == chipid_list[i]) > + return true; > + } > + > + return false; > +} > + > +static void sp_waiting_cable_plug_process(struct anx78xx *anx78xx) > +{ > + sp_tx_variable_init(); > + anx78xx_poweron(anx78xx); > + goto_next_system_state(anx78xx); > +} > + > +/* > + * Check if it is ANALOGIX dongle. > + */ > +static const u8 ANX_OUI[3] = {0x00, 0x22, 0xb9}; > + > +static u8 is_anx_dongle(struct anx78xx *anx78xx) Return a bool. > +{ > + u8 buf[3]; > + > + /* 0x0500~0x0502: BRANCH_IEEE_OUI */ > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x05, 0x00, 3, buf); > + > + if (!memcmp(buf, ANX_OUI, 3)) > + return 1; > + > + return 0; > +} > + > +static void sp_tx_get_rx_bw(struct anx78xx *anx78xx, u8 *bw) > +{ > + if (is_anx_dongle(anx78xx)) > + *bw = LINK_6P75G; /* just for debug */ > + else > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x00, > + DPCD_MAX_LINK_RATE, 1, bw); I don't understand what this does, and what is the "just for debug" part. Also, if nothing can fail, return the bandwidth directly. > +} > + > +static u8 sp_tx_get_cable_type(struct anx78xx *anx78xx, > + enum cable_type_status det_cable_type_state) > +{ > + struct device *dev = &anx78xx->client->dev; > + > + u8 ds_port_preset; > + u8 aux_status; > + u8 data_buf[16]; > + u8 cur_cable_type; > + > + ds_port_preset = 0; > + cur_cable_type = DWN_STRM_IS_NULL; > + > + aux_status = sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x00, 0x05, 1, > + &ds_port_preset); > + > + dev_dbg(dev, "DPCD 0x005: %x\n", (int)ds_port_preset); Cast is not necessary. > + > + switch (det_cable_type_state) { > + case CHECK_AUXCH: > + if (aux_status == 0) { > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x00, 0, 0x0c, > + data_buf); > + det_cable_type_state = GETTED_CABLE_TYPE; > + } else { > + dev_err(dev, "AUX access error\n"); > + break; > + } > + case GETTED_CABLE_TYPE: > + switch ((ds_port_preset & (BIT(1) | BIT(2))) >> 1) { > + case 0x00: > + cur_cable_type = DWN_STRM_IS_DIGITAL; > + dev_dbg(dev, "Downstream is DP dongle.\n"); > + break; > + case 0x01: > + case 0x03: > + cur_cable_type = DWN_STRM_IS_ANALOG; > + dev_dbg(dev, "Downstream is VGA dongle.\n"); > + break; > + case 0x02: > + cur_cable_type = DWN_STRM_IS_HDMI; > + dev_dbg(dev, "Downstream is HDMI dongle.\n"); > + break; > + default: > + cur_cable_type = DWN_STRM_IS_NULL; > + dev_err(dev, "Downstream can not recognized.\n"); > + break; > + } > + default: > + break; > + } > + return cur_cable_type; > +} > + > +static u8 sp_tx_get_dp_connection(struct anx78xx *anx78xx) bool? > +{ > + u8 regval; > + > + if (sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, > + DPCD_SINK_COUNT, 1, ®val)) > + return 0; > + > + if (regval & 0x1f) { if (!(regval & 0x1f)) return 0; > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x00, 0x04, 1, ®val); > + if (regval & 0x20) { > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x06, 0x00, 1, > + ®val); > + /* > + * Bit 5 = SET_DN_DEVICE_DP_PWR_5V > + * Bit 6 = SET_DN_DEVICE_DP_PWR_12V > + * Bit 7 = SET_DN_DEVICE_DP_PWR_18V > + */ > + regval = regval & 0x1f; > + sp_tx_aux_dpcdwrite_byte(anx78xx, 0x00, 0x06, 0x00, > + regval | 0x20); > + } > + return 1; > + } else { > + return 0; > + } > +} > + > +static void sp_sink_connection(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + > + switch (sp.tx_sc_state) { > + case SC_INIT: > + sp.tx_sc_state++; > + case SC_CHECK_CABLE_TYPE: > + case SC_WAITTING_CABLE_TYPE: > + default: > + if (sp_tx_get_cable_type(anx78xx, CHECK_AUXCH) == > + DWN_STRM_IS_NULL) { > + sp.tx_sc_state++; > + if (sp.tx_sc_state >= SC_WAITTING_CABLE_TYPE) { WAITING. +. Also, you are hiding the logic in the enum definition: you are doing 5 attempts before giving up. Please find another way. > + sp.tx_sc_state = SC_NOT_CABLE; > + dev_dbg(dev, "Can not get cable type!\n"); > + } > + break; > + } > + > + sp.tx_sc_state = SC_SINK_CONNECTED; > + case SC_SINK_CONNECTED: > + if (sp_tx_get_dp_connection(anx78xx)) > + goto_next_system_state(anx78xx); > + break; > + case SC_NOT_CABLE: > + sp_vbus_power_off(anx78xx); > + reg_hardware_reset(anx78xx); > + break; > + } > +} > + > +/******************start EDID process********************/ > +static void sp_tx_enable_video_input(struct anx78xx *anx78xx, u8 enable) bool enable (please replace all u8 by bool where appropriate, I'll ignore them from now on) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 regval; > + > + sp_read_reg(anx78xx, TX_P2, VID_CTRL1, ®val); > + if (enable) { > + sp_write_reg(anx78xx, TX_P2, VID_CTRL1, > + (regval & 0xf7) | VIDEO_EN); > + dev_dbg(dev, "Slimport Video is enabled!\n"); > + > + } else { > + sp_write_reg(anx78xx, TX_P2, VID_CTRL1, regval & ~VIDEO_EN); > + dev_dbg(dev, "Slimport Video is disabled!\n"); > + } > +} > + > +static u8 sp_get_edid_detail(u8 *data_buf) get_edid_bandwidth > +{ > + u16 pixclock_edid; > + > + pixclock_edid = (((u16)data_buf[1] << 8) | ((u16)data_buf[0] & 0xff)); > + if (pixclock_edid <= 5300) > + return LINK_1P62G; > + else if ((pixclock_edid > 5300) && (pixclock_edid <= 8900)) > 5300 already covered in previous test. > + return LINK_2P7G; > + else if ((pixclock_edid > 8900) && (pixclock_edid <= 18000)) > + return LINK_5P4G; > + else > + return LINK_6P75G; > +} > + > +static u8 sp_parse_edid_to_get_bandwidth(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 i, bandwidth, temp; > + > + bandwidth = LINK_1P62G; > + for (i = 0; i < 4; i++) { > + if (sp.edid_blocks[0x36 + 0x12 * i] == 0) > + break; > + temp = sp_get_edid_detail(sp.edid_blocks + 0x36 + 0x12 * i); > + dev_dbg(dev, "bandwidth via EDID : %x\n", temp); > + if (bandwidth < temp) > + bandwidth = temp; > + if (bandwidth >= LINK_6P75G) > + break; > + } > + > + return bandwidth; > +} > + > +static void sp_tx_aux_wr(struct anx78xx *anx78xx, u8 offset) > +{ > + sp_write_reg(anx78xx, TX_P0, BUF_DATA_0, offset); > + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, 0x04); > + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, AUX_OP_EN); > + sp_wait_aux_op_finish(anx78xx, &sp.edid_break); > +} > + > +static void sp_tx_aux_rd(struct anx78xx *anx78xx, u8 len_cmd) > +{ > + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, len_cmd); > + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, AUX_OP_EN); > + sp_wait_aux_op_finish(anx78xx, &sp.edid_break); > +} > + > +static u8 sp_tx_get_edid_block(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 regval; > + > + sp_tx_aux_wr(anx78xx, 0x7e); > + sp_tx_aux_rd(anx78xx, 0x01); > + sp_read_reg(anx78xx, TX_P0, BUF_DATA_0, ®val); > + dev_dbg(dev, "EDID Block = %d\n", regval + 1); > + > + if (regval > 3) > + regval = 1; > + return regval; > +} > + > +static void sp_edid_read(struct anx78xx *anx78xx, u8 offset, > + u8 *pblock_buf) > +{ > + u8 data_cnt, error_cnt; > + u8 regval; > + > + sp_tx_aux_wr(anx78xx, offset); > + sp_tx_aux_rd(anx78xx, 0xf5); > + data_cnt = 0; > + error_cnt = 0; > + > + while ((data_cnt) < 16) { data_cnt < 16 > + sp_read_reg(anx78xx, TX_P0, BUF_DATA_COUNT, ®val); > + > + if (regval & 0x1f) { > + data_cnt = data_cnt + (regval & 0x1f); > + do { > + sp_read_reg(anx78xx, TX_P0, > + BUF_DATA_0 + regval - 1, > + &pblock_buf[regval - 1]); > + } while (--regval); I think this would gain in clarity if you used a for loop. > + } else { > + if (error_cnt++ <= 2) { > + sp_tx_rst_aux(anx78xx); > + regval = 0x05 | ((0x0f - data_cnt) << 4); > + sp_tx_aux_rd(anx78xx, regval); > + } else { > + sp.edid_break = 1; > + break; > + } > + } > + } > + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, 0x01); > + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, ADDR_ONLY_BIT | AUX_OP_EN); > + sp_wait_aux_op_finish(anx78xx, &sp.edid_break); > + sp_tx_addronly_set(anx78xx, 0); > +} > + > +static void sp_tx_edid_read_initial(struct anx78xx *anx78xx) > +{ > + sp_write_reg(anx78xx, TX_P0, AUX_ADDR_7_0, 0x50); > + sp_write_reg(anx78xx, TX_P0, AUX_ADDR_15_8, 0); > + sp_write_reg_and(anx78xx, TX_P0, AUX_ADDR_19_16, 0xf0); > +} > + > +static void sp_seg_edid_read(struct anx78xx *anx78xx, > + u8 segment, u8 offset) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 regval, cnt; > + int i; > + > + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, 0x04); > + > + sp_write_reg(anx78xx, TX_P0, AUX_ADDR_7_0, 0x30); > + > + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, ADDR_ONLY_BIT | AUX_OP_EN); > + > + sp_read_reg(anx78xx, TX_P0, AUX_CTRL2, ®val); > + > + sp_wait_aux_op_finish(anx78xx, &sp.edid_break); > + sp_read_reg(anx78xx, TX_P0, AUX_CTRL, ®val); > + > + sp_write_reg(anx78xx, TX_P0, BUF_DATA_0, segment); > + > + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, 0x04); > + > + sp_write_reg_and_or(anx78xx, TX_P0, AUX_CTRL2, ~ADDR_ONLY_BIT, > + AUX_OP_EN); > + cnt = 0; > + sp_read_reg(anx78xx, TX_P0, AUX_CTRL2, ®val); > + while (regval & AUX_OP_EN) { > + usleep_range(1000, 2000); > + cnt++; > + if (cnt == 10) { > + dev_err(dev, "read AUX_CTRL2 failed.\n"); > + sp_tx_rst_aux(anx78xx); > + cnt = 0; That does not seem necessary... > + sp.edid_break = 1; You are using sp.edid_break as a return value, please return an int and drop that variable. > + return; > + } > + sp_read_reg(anx78xx, TX_P0, AUX_CTRL2, ®val); > + } > + > + sp_write_reg(anx78xx, TX_P0, AUX_ADDR_7_0, 0x50); > + > + sp_tx_aux_wr(anx78xx, offset); > + > + sp_tx_aux_rd(anx78xx, 0xf5); > + cnt = 0; > + for (i = 0; i < 16; i++) { > + sp_read_reg(anx78xx, TX_P0, BUF_DATA_COUNT, ®val); > + while ((regval & 0x1f) == 0) { > + usleep_range(2000, 4000); > + cnt++; > + sp_read_reg(anx78xx, TX_P0, BUF_DATA_COUNT, ®val); > + if (cnt == 10) { > + dev_err(dev, > + "read BUF_DATA_COUNT failed.\n"); > + dev_dbg(dev, "read break"); > + sp_tx_rst_aux(anx78xx); > + sp.edid_break = 1; > + return; > + } > + } > + > + sp_read_reg(anx78xx, TX_P0, BUF_DATA_0 + i, ®val); > + } > + > + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, 0x01); > + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, ADDR_ONLY_BIT | AUX_OP_EN); > + sp_write_reg_and(anx78xx, TX_P0, AUX_CTRL2, ~ADDR_ONLY_BIT); > + sp_read_reg(anx78xx, TX_P0, AUX_CTRL2, ®val); > + > + cnt = 0; > + while (regval & AUX_OP_EN) { > + usleep_range(1000, 2000); > + cnt++; > + if (cnt == 10) { > + dev_err(dev, "read AUX_CTRL2 failed.\n"); > + sp_tx_rst_aux(anx78xx); > + cnt = 0; > + sp.edid_break = 1; > + return; > + } > + sp_read_reg(anx78xx, TX_P0, AUX_CTRL2, ®val); > + } > +} > + > +static bool sp_edid_checksum_result(u8 *pbuf) > +{ > + u8 cnt, checksum; > + > + checksum = 0; > + > + for (cnt = 0; cnt < 0x80; cnt++) > + checksum = checksum + pbuf[cnt]; > + > + sp.edid_checksum = checksum - pbuf[0x7f]; > + sp.edid_checksum = ~sp.edid_checksum + 1; > + > + return checksum == 0 ? 1 : 0; return checksum == 0; > +} > + > +static void sp_check_edid_data(struct anx78xx *anx78xx, u8 *pbuf) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 i; > + > + if ((pbuf[0] == 0x00) && (pbuf[1] == 0xff) && > + (pbuf[2] == 0xff) && (pbuf[3] == 0xff) && > + (pbuf[4] == 0xff) && (pbuf[5] == 0xff) && > + (pbuf[6] == 0xff) && (pbuf[7] == 0x00)) > + dev_dbg(dev, "Good EDID header!\n"); > + else > + dev_err(dev, "Bad EDID header!\n"); > + > + for (i = 0; i <= (pbuf[0x7e] > 1 ? 1 : pbuf[0x7e]); i++) { > + if (!sp_edid_checksum_result(pbuf + i * 128)) > + dev_err(dev, "Block %x edid checksum error\n", i); > + else > + dev_dbg(dev, "Block %x edid checksum OK\n", i); > + } > +} > + > +static void sp_tx_edid_read(struct anx78xx *anx78xx, u8 *pedid_blocks_buf) pedid_blocks_buf is always sp.edid_blocks. > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 offset = 0; > + u8 count, blocks_num; > + u8 pblock_buf[16]; > + u8 i, j, regval; > + > + sp.edid_break = 0; > + sp_tx_edid_read_initial(anx78xx); > + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, 0x04); > + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, 0x03); > + sp_wait_aux_op_finish(anx78xx, &sp.edid_break); > + sp_tx_addronly_set(anx78xx, 0); > + > + blocks_num = sp_tx_get_edid_block(anx78xx); > + > + count = 0; > + do { > + switch (count) { > + case 0: > + case 1: > + for (i = 0; i < 8; i++) { > + offset = (i + count * 8) * 16; > + sp_edid_read(anx78xx, offset, pblock_buf); > + if (sp.edid_break == 1) > + break; > + for (j = 0; j < 16; j++) { > + pedid_blocks_buf[offset + j] > + = pblock_buf[j]; > + } > + } > + break; > + case 2: > + case 3: > + if (count == 2) > + offset = 0x00; > + else /* count == 3 */ > + offset = 0x80; > + for (j = 0; j < 8; j++) { > + if (sp.edid_break == 1) > + break; > + sp_seg_edid_read(anx78xx, count / 2, offset); > + offset = offset + 0x10; > + } > + break; > + default: > + break; > + } > + count++; > + if (sp.edid_break == 1) > + break; > + } while (blocks_num >= count); > + > + sp_tx_rst_aux(anx78xx); > + if (sp.read_edid_flag == 0) { > + sp_check_edid_data(anx78xx, pedid_blocks_buf); > + sp.read_edid_flag = 1; > + } > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x18, 1, ®val); > + if (regval & 0x04) { > + dev_dbg(dev, "check sum = %.2x\n", sp.edid_checksum); > + regval = sp.edid_checksum; > + sp_tx_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x02, 0x61, 1, > + ®val); > + sp.tx_test_edid = 1; > + regval = 0x04; > + sp_tx_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x02, 0x60, 1, > + ®val); > + dev_dbg(dev, "Test EDID done\n"); > + } > +} > + > +static bool sp_check_with_pre_edid(struct anx78xx *anx78xx, u8 *org_buf) org_buf is always sp.edid_blocks, so I'm not sure what's the point of the parameter. > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 i; > + u8 buf[16]; > + bool ret = false; > + > + sp.edid_break = 0; > + sp_tx_edid_read_initial(anx78xx); > + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, 0x04); > + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, 0x03); > + sp_wait_aux_op_finish(anx78xx, &sp.edid_break); > + sp_tx_addronly_set(anx78xx, 0); > + > + sp_edid_read(anx78xx, 0x70, buf); > + > + if (sp.edid_break == 0) { > + for (i = 0; i < 16; i++) { > + if (org_buf[0x70 + i] != buf[i]) { > + dev_dbg(dev, "%s\n", > + "different checksum and blocks num\n"); > + goto return_point; > + } > + } > + } else { > + goto return_point; > + } > + > + sp_edid_read(anx78xx, 0x08, buf); > + if (sp.edid_break == 0) { > + for (i = 0; i < 16; i++) { > + if (org_buf[i + 8] != buf[i]) { > + dev_dbg(dev, "different edid information\n"); > + goto return_point; > + } > + } > + } else { > + goto return_point; > + } > + > + ret = true; > +return_point: > + sp_tx_rst_aux(anx78xx); > + > + return ret; > +} > + > +static void sp_edid_process(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 temp_value, temp_value1; > + u8 i; > + > + dev_dbg(dev, "edid_process\n"); > + > + if (sp.read_edid_flag == 1) > + if (!sp_check_with_pre_edid(anx78xx, sp.edid_blocks)) > + sp.read_edid_flag = 0; > + > + if (sp.read_edid_flag == 0) { boolean: !sp.read_edid_flag > + sp_tx_edid_read(anx78xx, sp.edid_blocks); > + if (sp.edid_break) > + dev_err(dev, "ERR:EDID corruption!\n"); > + } > + > + /* Release the HPD after the OTP loaddown */ > + for (i = 0; i < 10; i++) { > + if (sp_i2c_read_byte(anx78xx, TX_P0, HDCP_KEY_STATUS) & 0x01) > + break; > + > + dev_dbg(dev, "waiting HDCP KEY loaddown\n"); > + usleep_range(1000, 2000); > + } > + > + sp_write_reg(anx78xx, RX_P0, HDMI_RX_INT_MASK1_REG, 0xe2); > + hdmi_rx_set_hpd(anx78xx, 1); > + dev_dbg(dev, "hdmi_rx_set_hpd 1 !\n"); > + > + hdmi_rx_set_termination(anx78xx, 1); > + > + sp_tx_get_rx_bw(anx78xx, &temp_value); > + dev_dbg(dev, "RX BW %x\n", temp_value); > + > + temp_value1 = sp_parse_edid_to_get_bandwidth(anx78xx); > + if (temp_value <= temp_value1) > + temp_value1 = temp_value; > + > + dev_dbg(dev, "set link bw in edid %x\n", temp_value1); > + sp.changed_bandwidth = temp_value1; > + goto_next_system_state(anx78xx); > +} > + > +/******************End EDID process********************/ > + > +/******************start Link training process********************/ > +static void sp_tx_lvttl_bit_mapping(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 regval, colorspace; > + u8 vid_bit; > + > + vid_bit = 0; > + sp_read_reg(anx78xx, RX_P1, HDMI_RX_AVI_DATA00_REG, &colorspace); > + colorspace &= 0x60; > + > + switch (((sp_i2c_read_byte(anx78xx, RX_P0, HDMI_RX_VIDEO_STATUS_REG1) > + & COLOR_DEPTH) >> 4)) { > + default: > + case HDMI_LEGACY: > + regval = IN_BPC_8BIT; > + vid_bit = 0; > + break; > + case HDMI_24BIT: > + regval = IN_BPC_8BIT; > + if (colorspace == 0x20) > + vid_bit = 5; > + else > + vid_bit = 1; > + break; > + case HDMI_30BIT: > + regval = IN_BPC_10BIT; > + if (colorspace == 0x20) > + vid_bit = 6; > + else > + vid_bit = 2; > + break; > + case HDMI_36BIT: > + regval = IN_BPC_12BIT; > + if (colorspace == 0x20) > + vid_bit = 6; > + else > + vid_bit = 3; > + break; > + } > + > + /* > + * For down sample video (12bit, 10bit ---> 8bit), > + * this register doesn't change > + */ > + if (sp.down_sample_en == 0) > + sp_write_reg_and_or(anx78xx, TX_P2, > + SP_TX_VID_CTRL2_REG, 0x8c, > + colorspace >> 5 | regval); > + > + /* Patch: for 10bit video must be set this value to 12bit by someone */ > + if (sp.down_sample_en == 1 && regval == IN_BPC_10BIT) > + vid_bit = 3; > + > + sp_write_reg_and_or(anx78xx, TX_P2, > + BIT_CTRL_SPECIFIC, 0x00, > + ENABLE_BIT_CTRL | vid_bit << 1); > + > + if (sp.tx_test_edid) { > + sp_write_reg_and(anx78xx, TX_P2, SP_TX_VID_CTRL2_REG, 0x8f); > + dev_dbg(dev, "***color space is set to 18bit***\n"); > + } > + > + if (colorspace) { > + sp_write_reg(anx78xx, TX_P0, SP_TX_VID_BLANK_SET1, 0x80); > + sp_write_reg(anx78xx, TX_P0, SP_TX_VID_BLANK_SET2, 0x00); > + sp_write_reg(anx78xx, TX_P0, SP_TX_VID_BLANK_SET3, 0x80); > + } else { > + sp_write_reg(anx78xx, TX_P0, SP_TX_VID_BLANK_SET1, 0x0); > + sp_write_reg(anx78xx, TX_P0, SP_TX_VID_BLANK_SET2, 0x0); > + sp_write_reg(anx78xx, TX_P0, SP_TX_VID_BLANK_SET3, 0x0); > + } > +} > + > +static unsigned long sp_tx_pclk_calc(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + unsigned long str_plck; > + u16 vid_counter; > + u8 regval; > + > + sp_read_reg(anx78xx, RX_P0, PCLK_HR_CNT2, ®val); > + vid_counter = regval << 8; > + sp_read_reg(anx78xx, RX_P0, PCLK_HR_CNT1, ®val); > + vid_counter |= regval; > + str_plck = (vid_counter * pxtal_data[XTAL_27M].xtal_clk_m10) >> 12; Single space before >> > + dev_dbg(dev, "PCLK = %d.%d\n", (u16)str_plck / 10, > + (u16)(str_plck - ((str_plck / 10) * 10))); str_pclk % 10, and no cast. > + return str_plck; > +} > + > +static u8 sp_tx_bw_lc_sel(struct anx78xx *anx78xx, unsigned long pclk) > +{ > + struct device *dev = &anx78xx->client->dev; > + unsigned long pixel_clk; > + u8 link; > + > + switch (((sp_i2c_read_byte(anx78xx, RX_P0, HDMI_RX_VIDEO_STATUS_REG1) > + & COLOR_DEPTH) >> 4)) { > + case HDMI_LEGACY: > + case HDMI_24BIT: > + default: > + pixel_clk = pclk; > + break; > + case HDMI_30BIT: > + pixel_clk = (pclk * 5) >> 2; > + break; > + case HDMI_36BIT: > + pixel_clk = (pclk * 3) >> 1; > + break; > + } > + > + dev_dbg(dev, "pixel_clk = %d.%d\n", (u16)pixel_clk / 10, > + (u16)(pixel_clk - ((pixel_clk / 10) * 10))); See above > + > + sp.down_sample_en = 0; > + if (pixel_clk <= 530) { > + link = LINK_1P62G; > + } else if ((530 < pixel_clk) && (pixel_clk <= 890)) { (530 < pixel_clk) is not necessary: you checked that just before. > + link = LINK_2P7G; > + } else if ((890 < pixel_clk) && (pixel_clk <= 1800)) { > + link = LINK_5P4G; > + } else { > + link = LINK_6P75G; > + if (pixel_clk > 2240) > + sp.down_sample_en = 1; > + } > + > + if (sp_tx_get_link_bw(anx78xx) != link) { > + sp.changed_bandwidth = link; > + dev_dbg(dev, > + "different bandwidth between sink and video %.2x", > + link); > + return 1; > + } > + return 0; > +} > + > +static void sp_tx_spread_enable(struct anx78xx *anx78xx, u8 benable) > +{ > + u8 regval; > + > + sp_read_reg(anx78xx, TX_P0, SP_TX_DOWN_SPREADING_CTRL1, ®val); > + > + if (benable) { > + regval |= SP_TX_SSC_DWSPREAD; > + sp_write_reg(anx78xx, TX_P0, SP_TX_DOWN_SPREADING_CTRL1, > + regval); > + > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x01, > + DPCD_DOWNSPREAD_CTRL, 1, ®val); > + regval |= SPREAD_AMPLITUDE; > + sp_tx_aux_dpcdwrite_byte(anx78xx, 0x00, 0x01, > + DPCD_DOWNSPREAD_CTRL, regval); > + } else { > + regval &= ~SP_TX_SSC_DISABLE; > + sp_write_reg(anx78xx, TX_P0, SP_TX_DOWN_SPREADING_CTRL1, > + regval); > + > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x01, > + DPCD_DOWNSPREAD_CTRL, 1, ®val); > + regval &= ~SPREAD_AMPLITUDE; > + sp_tx_aux_dpcdwrite_byte(anx78xx, 0x00, 0x01, > + DPCD_DOWNSPREAD_CTRL, regval); > + } > +} > + > +static void sp_tx_config_ssc(struct anx78xx *anx78xx, > + enum sp_ssc_dep sscdep) > +{ > + sp_write_reg(anx78xx, TX_P0, SP_TX_DOWN_SPREADING_CTRL1, 0x0); > + sp_write_reg(anx78xx, TX_P0, SP_TX_DOWN_SPREADING_CTRL1, sscdep); > + sp_tx_spread_enable(anx78xx, 1); > +} > + > +static void sp_tx_enhancemode_set(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 regval; > + > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x00, DPCD_MAX_LANE_COUNT, > + 1, ®val); > + if (regval & ENHANCED_FRAME_CAP) { > + sp_write_reg_or(anx78xx, TX_P0, SP_TX_SYS_CTRL4_REG, > + ENHANCED_MODE); > + > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x01, > + DPCD_LANE_COUNT_SET, 1, ®val); > + regval |= ENHANCED_FRAME_EN; > + sp_tx_aux_dpcdwrite_byte(anx78xx, 0x00, 0x01, > + DPCD_LANE_COUNT_SET, regval); > + > + dev_dbg(dev, "Enhance mode enabled\n"); > + } else { > + sp_write_reg_and(anx78xx, TX_P0, SP_TX_SYS_CTRL4_REG, > + ~ENHANCED_MODE); > + > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x01, > + DPCD_LANE_COUNT_SET, 1, ®val); > + > + regval &= ~ENHANCED_FRAME_EN; > + sp_tx_aux_dpcdwrite_byte(anx78xx, 0x00, 0x01, > + DPCD_LANE_COUNT_SET, regval); > + > + dev_dbg(dev, "Enhance mode disabled\n"); > + } This could be massively simplfied if you used your bit_ctl wrapper. > +} > + > +static u16 sp_tx_link_err_check(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + u16 errl = 0, errh = 0; > + u8 bytebuf[2]; > + > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x10, 2, bytebuf); > + usleep_range(5000, 10000); > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x10, 2, bytebuf); > + errh = bytebuf[1]; > + > + if (errh & 0x80) { > + errl = bytebuf[0]; > + errh = (errh & 0x7f) << 8; > + errl = errh + errl; return errl; Then I think you don't need errh/errl intermediate variables. > + } > + > + dev_err(dev, " Err of Lane = %d\n", errl); > + return errl; return 0; > +} > + > +static void sp_lt_finish(struct anx78xx *anx78xx, u8 temp_value) > +{ > + struct device *dev = &anx78xx->client->dev; > + > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x02, 1, &temp_value); > + if ((temp_value & 0x07) == 0x07) { > + /* > + * if there is link error, > + * adjust pre-emphsis to check error again. > + * If there is no error,keep the setting, > + * otherwise use 400mv0db > + */ > + if (!sp.tx_test_lt) { > + if (sp_tx_link_err_check(anx78xx)) { > + sp_read_reg(anx78xx, TX_P0, > + SP_TX_LT_SET_REG, &temp_value); > + if (!(temp_value & MAX_PRE_REACH)) { > + sp_write_reg(anx78xx, TX_P0, > + SP_TX_LT_SET_REG, > + temp_value + 0x08); > + if (sp_tx_link_err_check(anx78xx)) > + sp_write_reg(anx78xx, TX_P0, > + SP_TX_LT_SET_REG, > + temp_value); > + } > + } > + > + temp_value = sp_tx_get_link_bw(anx78xx); > + if (temp_value == sp.changed_bandwidth) { > + dev_dbg(dev, "LT succeed, bw: %.2x", > + temp_value); > + dev_dbg(dev, "Lane0 Set: %.2x\n", > + sp_i2c_read_byte(anx78xx, TX_P0, > + SP_TX_LT_SET_REG)); > + sp.tx_lt_state = LT_INIT; > + goto_next_system_state(anx78xx); > + } else { > + dev_dbg(dev, "cur:%.2x, per:%.2x\n", > + temp_value, > + sp.changed_bandwidth); > + sp.tx_lt_state = LT_ERROR; > + } > + } else { > + sp.tx_test_lt = 0; > + sp.tx_lt_state = LT_INIT; > + goto_next_system_state(anx78xx); > + } > + } else { > + dev_dbg(dev, "LANE0 Status error: %.2x\n", > + temp_value & 0x07); > + sp.tx_lt_state = LT_ERROR; > + } > +} > + > +static void sp_link_training(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 value, regval; > + > + dev_dbg(dev, "sp.tx_lt_state : %x\n", (int)sp.tx_lt_state); > + > + switch (sp.tx_lt_state) { > + case LT_INIT: > + sp_block_power_ctrl(anx78xx, SP_TX_PWR_VIDEO, SP_POWER_ON); > + sp_tx_video_mute(anx78xx, 1); > + sp_tx_enable_video_input(anx78xx, 0); > + sp.tx_lt_state++; > + /* fallthrough */ > + case LT_WAIT_PLL_LOCK: > + if (!sp_tx_get_pll_lock_status(anx78xx)) { > + sp_read_reg(anx78xx, TX_P0, SP_TX_PLL_CTRL_REG, > + &value); > + > + value |= PLL_RST; > + sp_write_reg(anx78xx, TX_P0, SP_TX_PLL_CTRL_REG, > + value); > + > + value &= ~PLL_RST; > + sp_write_reg(anx78xx, TX_P0, SP_TX_PLL_CTRL_REG, > + value); > + > + dev_dbg(dev, "PLL not lock!\n"); > + } else { > + sp.tx_lt_state = LT_CHECK_LINK_BW; > + } > + SP_BREAK(LT_WAIT_PLL_LOCK, sp.tx_lt_state); These control-flow modifying macros make the code very hard to read, please remove. > + /* fallthrough */ > + case LT_CHECK_LINK_BW: > + sp_tx_get_rx_bw(anx78xx, &value); > + if (value < sp.changed_bandwidth) { > + dev_dbg(dev, "****Over bandwidth****\n"); > + sp.changed_bandwidth = value; > + } else { > + sp.tx_lt_state++; > + } > + /* fallthrough */ > + case LT_START: > + if (sp.tx_test_lt) { > + sp.changed_bandwidth = sp.tx_test_bw; > + sp_write_reg_and(anx78xx, TX_P2, SP_TX_VID_CTRL2_REG, > + 0x8f); > + } else { > + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, 0x00); > + } > + > + sp_write_reg_and(anx78xx, TX_P0, SP_TX_ANALOG_PD_REG, > + ~CH0_PD); > + > + sp_tx_config_ssc(anx78xx, SSC_DEP_4000PPM); > + sp_tx_set_link_bw(anx78xx, sp.changed_bandwidth); > + sp_tx_enhancemode_set(anx78xx); > + > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x00, 0x00, 0x01, > + ®val); > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x06, 0x00, 0x01, > + &value); > + if (regval >= 0x12) > + value &= 0xf8; > + else > + value &= 0xfc; > + value |= 0x01; > + sp_tx_aux_dpcdwrite_byte(anx78xx, 0x00, 0x06, 0x00, value); > + > + sp_write_reg(anx78xx, TX_P0, LT_CTRL, SP_TX_LT_EN); > + sp.tx_lt_state = LT_WAITTING_FINISH; > + /* fallthrough */ > + case LT_WAITTING_FINISH: > + /* here : waiting interrupt to change training state. */ > + break; > + case LT_ERROR: > + sp_write_reg_or(anx78xx, TX_P2, RST_CTRL2, SERDES_FIFO_RST); > + msleep(20); > + sp_write_reg_and(anx78xx, TX_P2, RST_CTRL2, ~SERDES_FIFO_RST); > + dev_err(dev, "LT ERROR Status: SERDES FIFO reset."); > + redo_cur_system_state(anx78xx); > + sp.tx_lt_state = LT_INIT; > + break; > + case LT_FINISH: > + sp_lt_finish(anx78xx, value); > + break; > + default: > + break; > + } > +} > + > +/******************End Link training process********************/ > + > +/******************Start Output video process********************/ > +static void sp_tx_set_colorspace(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 color_space; > + > + if (sp.down_sample_en) { > + sp_read_reg(anx78xx, RX_P1, HDMI_RX_AVI_DATA00_REG, > + &color_space); > + color_space &= 0x60; > + if (color_space == 0x20) { > + dev_dbg(dev, "YCbCr4:2:2 ---> PASS THROUGH.\n"); > + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL6_REG, > + 0x00); > + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL5_REG, > + 0x00); > + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL2_REG, > + 0x11); > + } else if (color_space == 0x40) { > + dev_dbg(dev, "YCbCr4:4:4 ---> YCbCr4:2:2\n"); > + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL6_REG, > + 0x41); > + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL5_REG, > + 0x00); > + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL2_REG, > + 0x12); > + } else if (color_space == 0x00) { > + dev_dbg(dev, "RGB4:4:4 ---> YCbCr4:2:2\n"); > + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL6_REG, > + 0x41); > + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL5_REG, > + 0x83); > + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL2_REG, > + 0x10); > + } > + } else { > + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL6_REG, 0x00); > + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL5_REG, 0x00); > + } > +} > + > +static void sp_tx_avi_setup(struct anx78xx *anx78xx) > +{ > + u8 regval; > + int i; > + > + for (i = 0; i < 13; i++) { > + sp_read_reg(anx78xx, RX_P1, (HDMI_RX_AVI_DATA00_REG + i), > + ®val); > + sp.tx_packet_avi.avi_data[i] = regval; > + } > +} > + > +static void sp_tx_load_packet(struct anx78xx *anx78xx, > + enum packets_type type) > +{ > + int i; > + u8 regval; > + > + switch (type) { > + case AVI_PACKETS: > + sp_write_reg(anx78xx, TX_P2, SP_TX_AVI_TYPE, 0x82); > + sp_write_reg(anx78xx, TX_P2, SP_TX_AVI_VER, 0x02); > + sp_write_reg(anx78xx, TX_P2, SP_TX_AVI_LEN, 0x0d); > + > + for (i = 0; i < 13; i++) { > + sp_write_reg(anx78xx, TX_P2, SP_TX_AVI_DB0 + i, > + sp.tx_packet_avi.avi_data[i]); > + } > + > + break; > + > + case SPD_PACKETS: > + sp_write_reg(anx78xx, TX_P2, SP_TX_SPD_TYPE, 0x83); > + sp_write_reg(anx78xx, TX_P2, SP_TX_SPD_VER, 0x01); > + sp_write_reg(anx78xx, TX_P2, SP_TX_SPD_LEN, 0x19); > + > + for (i = 0; i < 25; i++) { > + sp_write_reg(anx78xx, TX_P2, SP_TX_SPD_DB0 + i, > + sp.tx_packet_spd.spd_data[i]); > + } > + > + break; > + > + case VSI_PACKETS: > + sp_write_reg(anx78xx, TX_P2, SP_TX_MPEG_TYPE, 0x81); > + sp_write_reg(anx78xx, TX_P2, SP_TX_MPEG_VER, 0x01); > + sp_read_reg(anx78xx, RX_P1, HDMI_RX_MPEG_LEN_REG, ®val); > + sp_write_reg(anx78xx, TX_P2, SP_TX_MPEG_LEN, regval); > + > + for (i = 0; i < 10; i++) { > + sp_write_reg(anx78xx, TX_P2, SP_TX_MPEG_DB0 + i, > + sp.tx_packet_mpeg.mpeg_data[i]); > + } > + > + break; > + case MPEG_PACKETS: > + sp_write_reg(anx78xx, TX_P2, SP_TX_MPEG_TYPE, 0x85); > + sp_write_reg(anx78xx, TX_P2, SP_TX_MPEG_VER, 0x01); > + sp_write_reg(anx78xx, TX_P2, SP_TX_MPEG_LEN, 0x0d); > + > + for (i = 0; i < 10; i++) { > + sp_write_reg(anx78xx, TX_P2, SP_TX_MPEG_DB0 + i, > + sp.tx_packet_mpeg.mpeg_data[i]); > + } > + > + break; > + case AUDIF_PACKETS: > + sp_write_reg(anx78xx, TX_P2, SP_TX_AUD_TYPE, 0x84); > + sp_write_reg(anx78xx, TX_P2, SP_TX_AUD_VER, 0x01); > + sp_write_reg(anx78xx, TX_P2, SP_TX_AUD_LEN, 0x0a); > + for (i = 0; i < 10; i++) { > + sp_write_reg(anx78xx, TX_P2, SP_TX_AUD_DB0 + i, > + sp.tx_audioinfoframe.pb_byte[i]); > + } > + > + break; > + > + default: > + break; > + } > +} > + > +static void sp_tx_config_packets(struct anx78xx *anx78xx, > + enum packets_type type) > +{ > + u8 regval; > + > + switch (type) { > + case AVI_PACKETS: > + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); > + regval &= ~AVI_IF_EN; > + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); > + sp_tx_load_packet(anx78xx, AVI_PACKETS); > + > + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); > + regval |= AVI_IF_UD; > + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); > + > + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); > + regval |= AVI_IF_EN; > + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); > + break; > + case SPD_PACKETS: > + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); > + regval &= ~SPD_IF_EN; > + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); > + sp_tx_load_packet(anx78xx, SPD_PACKETS); > + > + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); > + regval |= SPD_IF_UD; > + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); > + > + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); > + regval |= SPD_IF_EN; > + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); > + break; > + case VSI_PACKETS: > + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); > + regval &= ~MPEG_IF_EN; > + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); > + > + sp_tx_load_packet(anx78xx, VSI_PACKETS); > + > + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); > + regval |= MPEG_IF_UD; > + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); > + > + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); > + regval |= MPEG_IF_EN; > + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); > + break; > + case MPEG_PACKETS: > + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); > + regval &= ~MPEG_IF_EN; > + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); > + > + sp_tx_load_packet(anx78xx, MPEG_PACKETS); > + > + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); > + regval |= MPEG_IF_UD; > + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); > + > + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); > + regval |= MPEG_IF_EN; > + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); > + break; > + case AUDIF_PACKETS: > + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); > + regval &= ~AUD_IF_EN; > + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); > + > + sp_tx_load_packet(anx78xx, AUDIF_PACKETS); > + > + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); > + regval |= AUD_IF_UP; > + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); > + > + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); > + regval |= AUD_IF_EN; > + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); > + break; > + default: > + break; > + } > +} > + > +static void sp_config_video_output(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 regval; > + > + switch (sp.tx_vo_state) { > + default: > + case VO_WAIT_VIDEO_STABLE: > + sp_read_reg(anx78xx, RX_P0, HDMI_RX_SYS_STATUS_REG, ®val); > + if ((regval & (TMDS_DE_DET | TMDS_CLOCK_DET)) == 0x03) { 0x03? Is that the same as (TMDS_DE_DET | TMDS_CLOCK_DET)? > + sp_tx_bw_lc_sel(anx78xx, sp_tx_pclk_calc(anx78xx)); > + sp_tx_enable_video_input(anx78xx, 0); > + sp_tx_avi_setup(anx78xx); > + sp_tx_config_packets(anx78xx, AVI_PACKETS); > + sp_tx_set_colorspace(anx78xx); > + sp_tx_lvttl_bit_mapping(anx78xx); > + if (sp_i2c_read_byte(anx78xx, RX_P0, RX_PACKET_REV_STA) > + & VSI_RCVD) > + sp_hdmi_rx_new_vsi_int(anx78xx); > + sp_tx_enable_video_input(anx78xx, 1); > + sp.tx_vo_state = VO_WAIT_TX_VIDEO_STABLE; > + } else { > + dev_dbg(dev, "HDMI input video not stable!\n"); > + } > + SP_BREAK(VO_WAIT_VIDEO_STABLE, sp.tx_vo_state); > + /* fallthrough */ > + case VO_WAIT_TX_VIDEO_STABLE: > + sp_read_reg(anx78xx, TX_P0, SP_TX_SYS_CTRL2_REG, ®val); > + sp_write_reg(anx78xx, TX_P0, SP_TX_SYS_CTRL2_REG, regval); > + sp_read_reg(anx78xx, TX_P0, SP_TX_SYS_CTRL2_REG, ®val); > + if (regval & CHA_STA) { > + dev_dbg(dev, "Stream clock not stable!\n"); > + } else { > + sp_read_reg(anx78xx, TX_P0, SP_TX_SYS_CTRL3_REG, > + ®val); > + sp_write_reg(anx78xx, TX_P0, SP_TX_SYS_CTRL3_REG, > + regval); > + sp_read_reg(anx78xx, TX_P0, SP_TX_SYS_CTRL3_REG, > + ®val); > + if (!(regval & STRM_VALID)) > + dev_err(dev, "video stream not valid!\n"); > + else > + sp.tx_vo_state = VO_CHECK_VIDEO_INFO; > + } > + SP_BREAK(VO_WAIT_TX_VIDEO_STABLE, sp.tx_vo_state); > + /* fallthrough */ > + case VO_CHECK_VIDEO_INFO: > + if (!sp_tx_bw_lc_sel(anx78xx, sp_tx_pclk_calc(anx78xx))) > + sp.tx_vo_state++; > + else > + sp_tx_set_sys_state(anx78xx, STATE_LINK_TRAINING); > + SP_BREAK(VO_CHECK_VIDEO_INFO, sp.tx_vo_state); > + /* fallthrough */ > + case VO_FINISH: > + sp_block_power_ctrl(anx78xx, SP_TX_PWR_AUDIO, SP_POWER_DOWN); > + hdmi_rx_mute_video(anx78xx, 0); > + sp_tx_video_mute(anx78xx, 0); > + sp_tx_show_information(anx78xx); > + goto_next_system_state(anx78xx); > + break; > + } > +} > + > +/******************End Output video process********************/ > + > +/******************Start HDCP process********************/ > +static inline void sp_tx_hdcp_encryption_disable(struct anx78xx *anx78xx) > +{ > + sp_write_reg_and(anx78xx, TX_P0, TX_HDCP_CTRL0, ~ENC_EN); > +} > + > +static inline void sp_tx_hdcp_encryption_enable(struct anx78xx *anx78xx) > +{ > + sp_write_reg_or(anx78xx, TX_P0, TX_HDCP_CTRL0, ENC_EN); > +} > + > +static void sp_tx_hw_hdcp_enable(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 regval; > + > + sp_write_reg_and(anx78xx, TX_P0, TX_HDCP_CTRL0, > + ~ENC_EN & ~HARD_AUTH_EN); > + sp_write_reg_or(anx78xx, TX_P0, TX_HDCP_CTRL0, > + HARD_AUTH_EN | BKSV_SRM_PASS | KSVLIST_VLD | ENC_EN); > + > + sp_read_reg(anx78xx, TX_P0, TX_HDCP_CTRL0, ®val); > + dev_dbg(dev, "TX_HDCP_CTRL0 = %.2x\n", regval); > + sp_write_reg(anx78xx, TX_P0, SP_TX_WAIT_R0_TIME, 0xb0); > + sp_write_reg(anx78xx, TX_P0, SP_TX_WAIT_KSVR_TIME, 0xc8); > + > + dev_dbg(dev, "Hardware HDCP is enabled.\n"); > +} > + > +static void sp_hdcp_process(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + > + switch (sp.hcdp_state) { > + case HDCP_CAPABLE_CHECK: > + sp.ds_vid_stb_cntr = 0; > + sp.hdcp_fail_count = 0; > + if (is_anx_dongle(anx78xx)) > + sp.hcdp_state = HDCP_WAITTING_VID_STB; > + else > + sp.hcdp_state = HDCP_HW_ENABLE; > + if (sp.block_en == 0) { > + if (sp_hdcp_cap_check(anx78xx) == 0) > + sp.hcdp_state = HDCP_NOT_SUPPORT; > + } > + /* > + * Just for debug, pin: P2-2 > + * There is a switch to disable/enable HDCP. > + */ > + sp.hcdp_state = HDCP_NOT_SUPPORT; > + /*****************************************/ > + SP_BREAK(HDCP_CAPABLE_CHECK, sp.hcdp_state); > + /* fallthrough */ > + case HDCP_WAITTING_VID_STB: > + msleep(100); > + sp.hcdp_state = HDCP_HW_ENABLE; > + SP_BREAK(HDCP_WAITTING_VID_STB, sp.hcdp_state); > + /* fallthrough */ > + case HDCP_HW_ENABLE: > + sp_tx_video_mute(anx78xx, 1); > + sp_tx_clean_hdcp_status(anx78xx); > + sp_block_power_ctrl(anx78xx, SP_TX_PWR_HDCP, SP_POWER_DOWN); > + msleep(20); > + sp_block_power_ctrl(anx78xx, SP_TX_PWR_HDCP, SP_POWER_ON); > + sp_write_reg(anx78xx, TX_P2, SP_COMMON_INT_MASK2, 0x01); > + msleep(50); > + sp_tx_hw_hdcp_enable(anx78xx); > + sp.hcdp_state = HDCP_WAITTING_FINISH; > + /* fallthrough */ > + case HDCP_WAITTING_FINISH: > + break; > + case HDCP_FINISH: > + sp_tx_hdcp_encryption_enable(anx78xx); > + hdmi_rx_mute_video(anx78xx, 0); > + sp_tx_video_mute(anx78xx, 0); > + goto_next_system_state(anx78xx); > + sp.hcdp_state = HDCP_CAPABLE_CHECK; > + dev_dbg(dev, "@@@@@@@hdcp_auth_pass@@@@@@\n"); > + break; > + case HDCP_FAILED: > + if (sp.hdcp_fail_count > 5) { > + sp_vbus_power_off(anx78xx); > + reg_hardware_reset(anx78xx); > + sp.hcdp_state = HDCP_CAPABLE_CHECK; > + sp.hdcp_fail_count = 0; > + dev_dbg(dev, "*********hdcp_auth_failed*********\n"); > + } else { > + sp.hdcp_fail_count++; > + sp.hcdp_state = HDCP_WAITTING_VID_STB; > + } > + break; > + default: > + case HDCP_NOT_SUPPORT: > + dev_dbg(dev, "Sink is not capable HDCP\n"); > + sp_block_power_ctrl(anx78xx, SP_TX_PWR_HDCP, > + SP_POWER_DOWN); > + sp_tx_video_mute(anx78xx, 0); > + goto_next_system_state(anx78xx); > + sp.hcdp_state = HDCP_CAPABLE_CHECK; > + break; > + } > +} > + > +/******************End HDCP process********************/ > + > +/******************Start Audio process********************/ > +static void sp_tx_audioinfoframe_setup(struct anx78xx *anx78xx) > +{ > + int i; > + u8 regval; > + > + sp_read_reg(anx78xx, RX_P1, HDMI_RX_AUDIO_TYPE_REG, ®val); > + sp.tx_audioinfoframe.type = regval; > + sp_read_reg(anx78xx, RX_P1, HDMI_RX_AUDIO_VER_REG, ®val); > + sp.tx_audioinfoframe.version = regval; > + sp_read_reg(anx78xx, RX_P1, HDMI_RX_AUDIO_LEN_REG, ®val); > + sp.tx_audioinfoframe.length = regval; > + > + for (i = 0; i < 11; i++) { > + sp_read_reg(anx78xx, RX_P1, HDMI_RX_AUDIO_DATA00_REG + i, > + ®val); > + sp.tx_audioinfoframe.pb_byte[i] = regval; > + } > +} > + > +static void sp_tx_enable_audio_output(struct anx78xx *anx78xx, u8 enable) > +{ > + u8 regval; > + > + sp_read_reg(anx78xx, TX_P0, SP_TX_AUD_CTRL, ®val); > + if (enable) { > + if (regval & AUD_EN) { > + regval &= ~AUD_EN; > + sp_write_reg(anx78xx, TX_P0, SP_TX_AUD_CTRL, regval); > + } > + sp_tx_audioinfoframe_setup(anx78xx); > + sp_tx_config_packets(anx78xx, AUDIF_PACKETS); > + > + regval |= AUD_EN; > + sp_write_reg(anx78xx, TX_P0, SP_TX_AUD_CTRL, regval); > + } else { > + regval &= ~AUD_EN; > + sp_write_reg(anx78xx, TX_P0, SP_TX_AUD_CTRL, regval); > + sp_write_reg_and(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ~AUD_IF_EN); > + } > +} > + > +static void sp_tx_config_audio(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 regval; > + int i; > + unsigned long m_aud, ls_clk = 0; > + unsigned long aud_freq = 0; > + > + sp_block_power_ctrl(anx78xx, SP_TX_PWR_AUDIO, SP_POWER_ON); > + sp_read_reg(anx78xx, RX_P0, AUD_SPDIF_CHST4, ®val); > + > + switch (regval & 0x0f) { > + case FS_FREQ_44100HZ: > + aud_freq = 44100; > + break; > + case FS_FREQ_48000HZ: > + aud_freq = 48000; > + break; > + case FS_FREQ_32000HZ: > + aud_freq = 32000; > + break; > + case FS_FREQ_88200HZ: > + aud_freq = 88200; > + break; > + case FS_FREQ_96000HZ: > + aud_freq = 96000; > + break; > + case FS_FREQ_176400HZ: > + aud_freq = 176400; > + break; > + case FS_FREQ_192000HZ: > + aud_freq = 192000; > + break; > + default: > + break; > + } > + > + switch (sp_tx_get_link_bw(anx78xx)) { > + case LINK_1P62G: > + ls_clk = 162000; > + break; > + case LINK_2P7G: > + ls_clk = 270000; > + break; > + case LINK_5P4G: > + ls_clk = 540000; > + break; > + case LINK_6P75G: > + ls_clk = 675000; > + break; > + default: > + break; > + } > + > + dev_dbg(dev, "aud_freq = %ld , LS_CLK = %ld\n", aud_freq, ls_clk); > + > + m_aud = ((512 * aud_freq) / ls_clk) * 32768; > + m_aud = m_aud + 0x05; > + sp_write_reg(anx78xx, TX_P1, SP_TX_AUD_INTERFACE_CTRL4, m_aud & 0xff); > + m_aud = m_aud >> 8; > + sp_write_reg(anx78xx, TX_P1, SP_TX_AUD_INTERFACE_CTRL5, m_aud & 0xff); > + sp_write_reg(anx78xx, TX_P1, SP_TX_AUD_INTERFACE_CTRL6, 0x00); > + > + sp_write_reg_and(anx78xx, TX_P1, SP_TX_AUD_INTERFACE_CTRL0, > + (u8)~AUD_INTERFACE_DISABLE); > + > + sp_write_reg_or(anx78xx, TX_P1, SP_TX_AUD_INTERFACE_CTRL2, > + M_AUD_ADJUST_ST); > + > + sp_read_reg(anx78xx, RX_P0, HDMI_STATUS, ®val); > + if (regval & HDMI_AUD_LAYOUT) > + sp_write_reg_or(anx78xx, TX_P2, SP_TX_AUD_CH_NUM_REG5, > + CH_NUM_8 | AUD_LAYOUT); > + else > + sp_write_reg_and(anx78xx, TX_P2, SP_TX_AUD_CH_NUM_REG5, > + (u8)~CH_NUM_8 & ~AUD_LAYOUT); > + > + /* transfer audio chaneel status from HDMI Rx to Slinmport Tx */ > + for (i = 0; i < 5; i++) { > + sp_read_reg(anx78xx, RX_P0, HDMI_RX_AUD_IN_CH_STATUS1_REG + i, > + ®val); > + sp_write_reg(anx78xx, TX_P2, SP_TX_AUD_CH_STATUS_REG1 + i, > + regval); > + } > + > + /* enable audio */ > + sp_tx_enable_audio_output(anx78xx, 1); > +} > + > +static void sp_config_audio_output(struct anx78xx *anx78xx) > +{ > + static u8 count; > + > + switch (sp.tx_ao_state) { > + default: > + case AO_INIT: > + case AO_CTS_RCV_INT: > + case AO_AUDIO_RCV_INT: > + if (!(sp_i2c_read_byte(anx78xx, RX_P0, HDMI_STATUS) > + & HDMI_MODE)) { > + sp.tx_ao_state = AO_INIT; > + goto_next_system_state(anx78xx); > + } > + break; > + case AO_RCV_INT_FINISH: > + if (count++ > 2) > + sp.tx_ao_state = AO_OUTPUT; > + else > + sp.tx_ao_state = AO_INIT; > + SP_BREAK(AO_INIT, sp.tx_ao_state); > + /* fallthrough */ > + case AO_OUTPUT: > + count = 0; > + sp.tx_ao_state = AO_INIT; > + hdmi_rx_mute_audio(anx78xx, 0); > + sp_tx_config_audio(anx78xx); > + goto_next_system_state(anx78xx); > + break; > + } > +} > + > +/******************End Audio process********************/ > + > +void sp_initialization(struct anx78xx *anx78xx) > +{ > + /* Waitting Hot plug event! */ > + if (!(sp.common_int_status.common_int[3] & PLUG)) > + return; > + > + sp.read_edid_flag = 0; > + > + /* Power on all modules */ > + sp_write_reg(anx78xx, TX_P2, SP_POWERD_CTRL_REG, 0x00); > + /* Driver Version */ > + sp_write_reg(anx78xx, TX_P1, FW_VER_REG, FW_VERSION); > + hdmi_rx_initialization(anx78xx); > + sp_tx_initialization(anx78xx); > + msleep(200); > + goto_next_system_state(anx78xx); > +} > + > +static void sp_hdcp_external_ctrl_flag_monitor(struct anx78xx *anx78xx) > +{ > + static u8 cur_flag; > + > + if (sp.block_en != cur_flag) { > + cur_flag = sp.block_en; > + system_state_change_with_case(anx78xx, STATE_HDCP_AUTH); > + } > +} > + > +static void sp_state_process(struct anx78xx *anx78xx) > +{ > + switch (sp.tx_system_state) { > + case STATE_WAITTING_CABLE_PLUG: > + sp_waiting_cable_plug_process(anx78xx); > + SP_BREAK(STATE_WAITTING_CABLE_PLUG, sp.tx_system_state); > + /* fallthrough */ > + case STATE_SP_INITIALIZED: > + sp_initialization(anx78xx); > + SP_BREAK(STATE_SP_INITIALIZED, sp.tx_system_state); > + /* fallthrough */ > + case STATE_SINK_CONNECTION: > + sp_sink_connection(anx78xx); > + SP_BREAK(STATE_SINK_CONNECTION, sp.tx_system_state); > + /* fallthrough */ > + case STATE_PARSE_EDID: > + sp_edid_process(anx78xx); > + SP_BREAK(STATE_PARSE_EDID, sp.tx_system_state); > + /* fallthrough */ > + case STATE_LINK_TRAINING: > + sp_link_training(anx78xx); > + SP_BREAK(STATE_LINK_TRAINING, sp.tx_system_state); > + /* fallthrough */ > + case STATE_VIDEO_OUTPUT: > + sp_config_video_output(anx78xx); > + SP_BREAK(STATE_VIDEO_OUTPUT, sp.tx_system_state); > + /* fallthrough */ > + case STATE_HDCP_AUTH: > + sp_hdcp_process(anx78xx); > + SP_BREAK(STATE_HDCP_AUTH, sp.tx_system_state); > + /* fallthrough */ > + case STATE_AUDIO_OUTPUT: > + sp_config_audio_output(anx78xx); > + SP_BREAK(STATE_AUDIO_OUTPUT, sp.tx_system_state); > + /* fallthrough */ > + case STATE_PLAY_BACK: > + SP_BREAK(STATE_PLAY_BACK, sp.tx_system_state); > + /* fallthrough */ > + default: > + break; > + } > +} > + > +/******************Start INT process********************/ > +static void sp_tx_int_rec(struct anx78xx *anx78xx) > +{ > + sp_read_reg(anx78xx, TX_P2, SP_COMMON_INT_STATUS1, > + &sp.common_int_status.common_int[0]); > + sp_write_reg(anx78xx, TX_P2, SP_COMMON_INT_STATUS1, > + sp.common_int_status.common_int[0]); > + > + sp_read_reg(anx78xx, TX_P2, SP_COMMON_INT_STATUS1 + 1, > + &sp.common_int_status.common_int[1]); > + sp_write_reg(anx78xx, TX_P2, SP_COMMON_INT_STATUS1 + 1, > + sp.common_int_status.common_int[1]); > + > + sp_read_reg(anx78xx, TX_P2, SP_COMMON_INT_STATUS1 + 2, > + &sp.common_int_status.common_int[2]); > + sp_write_reg(anx78xx, TX_P2, SP_COMMON_INT_STATUS1 + 2, > + sp.common_int_status.common_int[2]); > + > + sp_read_reg(anx78xx, TX_P2, SP_COMMON_INT_STATUS1 + 3, > + &sp.common_int_status.common_int[3]); > + sp_write_reg(anx78xx, TX_P2, SP_COMMON_INT_STATUS1 + 3, > + sp.common_int_status.common_int[3]); > + > + sp_read_reg(anx78xx, TX_P2, SP_COMMON_INT_STATUS1 + 6, > + &sp.common_int_status.common_int[4]); > + sp_write_reg(anx78xx, TX_P2, SP_COMMON_INT_STATUS1 + 6, > + sp.common_int_status.common_int[4]); Loop please. > +} > + > +static void sp_hdmi_rx_int_rec(struct anx78xx *anx78xx) > +{ > + sp_read_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS1_REG, > + &sp.hdmi_rx_int_status.hdmi_rx_int[0]); > + sp_write_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS1_REG, > + sp.hdmi_rx_int_status.hdmi_rx_int[0]); > + sp_read_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS2_REG, > + &sp.hdmi_rx_int_status.hdmi_rx_int[1]); > + sp_write_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS2_REG, > + sp.hdmi_rx_int_status.hdmi_rx_int[1]); > + sp_read_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS3_REG, > + &sp.hdmi_rx_int_status.hdmi_rx_int[2]); > + sp_write_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS3_REG, > + sp.hdmi_rx_int_status.hdmi_rx_int[2]); > + sp_read_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS4_REG, > + &sp.hdmi_rx_int_status.hdmi_rx_int[3]); > + sp_write_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS4_REG, > + sp.hdmi_rx_int_status.hdmi_rx_int[3]); > + sp_read_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS5_REG, > + &sp.hdmi_rx_int_status.hdmi_rx_int[4]); > + sp_write_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS5_REG, > + sp.hdmi_rx_int_status.hdmi_rx_int[4]); > + sp_read_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS6_REG, > + &sp.hdmi_rx_int_status.hdmi_rx_int[5]); > + sp_write_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS6_REG, > + sp.hdmi_rx_int_status.hdmi_rx_int[5]); > + sp_read_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS7_REG, > + &sp.hdmi_rx_int_status.hdmi_rx_int[6]); > + sp_write_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS7_REG, > + sp.hdmi_rx_int_status.hdmi_rx_int[6]); Loop. > +} > + > +static void sp_int_rec(struct anx78xx *anx78xx) > +{ > + sp_tx_int_rec(anx78xx); > + sp_hdmi_rx_int_rec(anx78xx); > +} > + > +/******************End INT process********************/ > + > +/******************Start task process********************/ > +static void sp_tx_pll_changed_int_handler(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + > + if (sp.tx_system_state >= STATE_LINK_TRAINING) { > + if (!sp_tx_get_pll_lock_status(anx78xx)) { > + dev_dbg(dev, "PLL:PLL not lock!\n"); > + sp_tx_set_sys_state(anx78xx, STATE_LINK_TRAINING); > + } > + } > +} > + > +static void sp_tx_hdcp_link_chk_fail_handler(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + > + system_state_change_with_case(anx78xx, STATE_HDCP_AUTH); > + > + dev_dbg(dev, "hdcp_link_chk_fail:HDCP Sync lost!\n"); > +} > + > +static void sp_tx_phy_auto_test(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 b_sw; > + u8 bytebuf[16]; > + > + /* DPCD 0x219 TEST_LINK_RATE */ > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x0, 0x02, 0x19, 1, bytebuf); > + dev_dbg(dev, "DPCD:0x00219 = %.2x\n", bytebuf[0]); > + switch (bytebuf[0]) { > + case LINK_1P62G: > + case LINK_2P7G: > + case LINK_5P4G: > + case LINK_6P75G: > + sp_tx_set_link_bw(anx78xx, bytebuf[0]); > + sp.tx_test_bw = bytebuf[0]; > + break; > + default: > + sp_tx_set_link_bw(anx78xx, LINK_6P75G); > + sp.tx_test_bw = LINK_6P75G; > + break; > + } > + > + /* DPCD 0x248 PHY_TEST_PATTERN */ > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x0, 0x02, 0x48, 1, bytebuf); > + dev_dbg(dev, "DPCD:0x00248 = %.2x\n", bytebuf[0]); > + switch (bytebuf[0]) { > + case 0: > + break; > + case 1: > + sp_write_reg(anx78xx, TX_P0, SP_TX_TRAINING_PTN_SET_REG, 0x04); > + break; > + case 2: > + sp_write_reg(anx78xx, TX_P0, SP_TX_TRAINING_PTN_SET_REG, 0x08); > + break; > + case 3: > + sp_write_reg(anx78xx, TX_P0, SP_TX_TRAINING_PTN_SET_REG, 0x0c); > + break; > + case 4: > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x50, 0xa, > + bytebuf); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_TEST_PATTERN_REG0, > + bytebuf[0]); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_TEST_PATTERN_REG1, > + bytebuf[1]); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_TEST_PATTERN_REG2, > + bytebuf[2]); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_TEST_PATTERN_REG3, > + bytebuf[3]); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_TEST_PATTERN_REG4, > + bytebuf[4]); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_TEST_PATTERN_REG5, > + bytebuf[5]); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_TEST_PATTERN_REG6, > + bytebuf[6]); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_TEST_PATTERN_REG7, > + bytebuf[7]); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_TEST_PATTERN_REG8, > + bytebuf[8]); > + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_TEST_PATTERN_REG9, > + bytebuf[9]); Loop. > + sp_write_reg(anx78xx, TX_P0, SP_TX_TRAINING_PTN_SET_REG, 0x30); > + break; > + case 5: > + sp_write_reg(anx78xx, TX_P0, ADDR_DP_CEP_TRAINING_CTRL0, 0x00); > + sp_write_reg(anx78xx, TX_P0, ADDR_DP_CEP_TRAINING_CTRL1, 0x01); > + sp_write_reg(anx78xx, TX_P0, SP_TX_TRAINING_PTN_SET_REG, 0x14); > + break; > + } > + > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x00, 0x03, 1, bytebuf); > + dev_dbg(dev, "DPCD:0x00003 = %.2x\n", bytebuf[0]); > + if (bytebuf[0] & 0x01) > + sp_tx_config_ssc(anx78xx, SSC_DEP_4000PPM); > + else > + sp_tx_spread_enable(anx78xx, 0); > + > + /* get swing and emphasis adjust request */ > + sp_read_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, &b_sw); > + > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x06, 1, bytebuf); > + dev_dbg(dev, "DPCD:0x00206 = %.2x\n", bytebuf[0]); > + switch (bytebuf[0] & 0x0f) { > + case 0x00: > + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, > + (b_sw & ~TX_SW_SET_MASK) | 0x00); > + break; > + case 0x01: > + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, > + (b_sw & ~TX_SW_SET_MASK) | 0x01); > + break; > + case 0x02: > + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, > + (b_sw & ~TX_SW_SET_MASK) | 0x02); > + break; > + case 0x03: > + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, > + (b_sw & ~TX_SW_SET_MASK) | 0x03); > + break; Those 4 are just | (bytebuf[0] & 0x0f) > + case 0x04: > + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, > + (b_sw & ~TX_SW_SET_MASK) | 0x08); > + break; > + case 0x05: > + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, > + (b_sw & ~TX_SW_SET_MASK) | 0x09); > + break; > + case 0x06: > + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, > + (b_sw & ~TX_SW_SET_MASK) | 0x0a); > + break; Those 4 are | ((bytebuf[0] & 0x0f) + 4) > + case 0x08: > + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, > + (b_sw & ~TX_SW_SET_MASK) | 0x10); > + break; > + case 0x09: > + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, > + (b_sw & ~TX_SW_SET_MASK) | 0x11); > + break; > + case 0x0c: > + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, > + (b_sw & ~TX_SW_SET_MASK) | 0x18); > + break; > + default: > + break; > + } > +} > + > +static void sp_hpd_irq_process(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 regval; > + u8 test_vector; > + u8 data_buf[6]; > + > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x00, 6, data_buf); > + dev_dbg(dev, "+++++++++++++Get HPD IRQ %x\n", (int)data_buf[1]); > + > + if (data_buf[1] != 0) > + sp_tx_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x02, > + DPCD_SERVICE_IRQ_VECTOR, 1, > + &data_buf[1]); > + > + /* HDCP IRQ */ > + if (data_buf[1] & CP_IRQ) { > + if (sp.hcdp_state > HDCP_WAITTING_FINISH || > + sp.tx_system_state > STATE_HDCP_AUTH) { > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x06, 0x80, 0x29, 1, > + ®val); > + if (regval & 0x04) { > + system_state_change_with_case(anx78xx, > + STATE_HDCP_AUTH); > + dev_dbg(dev, "IRQ:_______HDCP Sync lost!\n"); > + } > + } > + } > + > + /* AUTOMATED TEST IRQ */ > + if (data_buf[1] & TEST_IRQ) { > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x18, 1, > + &test_vector); > + > + if (test_vector & 0x01) { > + sp.tx_test_lt = 1; > + > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x19, 1, > + ®val); > + switch (regval) { > + case LINK_1P62G: > + case LINK_2P7G: > + case LINK_5P4G: > + case LINK_6P75G: > + sp_tx_set_link_bw(anx78xx, regval); > + sp.tx_test_bw = regval; > + break; > + default: > + sp_tx_set_link_bw(anx78xx, LINK_6P75G); > + sp.tx_test_bw = LINK_6P75G; > + break; > + } > + > + dev_dbg(dev, " test_bw = %.2x\n", sp.tx_test_bw); > + > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x60, 1, > + ®val); > + regval = regval | TEST_ACK; > + sp_tx_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x02, 0x60, 1, > + ®val); > + > + dev_dbg(dev, "Set TEST_ACK!\n"); > + if (sp.tx_system_state >= STATE_LINK_TRAINING) { > + sp.tx_lt_state = LT_INIT; > + sp_tx_set_sys_state(anx78xx, > + STATE_LINK_TRAINING); > + } > + dev_dbg(dev, "IRQ:test-LT request!\n"); > + } > + > + if (test_vector & 0x02) { > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x60, 1, > + ®val); > + regval = regval | TEST_ACK; > + sp_tx_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x02, 0x60, 1, > + ®val); > + } > + if (test_vector & 0x04) { > + if (sp.tx_system_state > STATE_PARSE_EDID) > + sp_tx_set_sys_state(anx78xx, STATE_PARSE_EDID); > + sp.tx_test_edid = 1; > + dev_dbg(dev, "Test EDID Requested!\n"); > + } > + > + if (test_vector & 0x08) { > + sp.tx_test_lt = 1; > + > + sp_tx_phy_auto_test(anx78xx); > + > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x60, 1, > + ®val); > + regval = regval | 0x01; > + sp_tx_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x02, 0x60, 1, > + ®val); > + } > + } > + > + if (sp.tx_system_state > STATE_LINK_TRAINING) { > + if (!(data_buf[4] & 0x01) || > + ((data_buf[2] & (0x01 | 0x04)) != 0x05)) { & 0x05) != 0x05 > + sp_tx_set_sys_state(anx78xx, STATE_LINK_TRAINING); > + dev_dbg(dev, "INT:re-LT request!\n"); > + return; > + } > + > + dev_dbg(dev, "Lane align %x\n", data_buf[4]); > + dev_dbg(dev, "Lane clock recovery %x\n", data_buf[2]); > + } > +} > + > +static void sp_tx_vsi_setup(struct anx78xx *anx78xx) > +{ > + u8 regval; > + int i; > + > + for (i = 0; i < 10; i++) { > + sp_read_reg(anx78xx, RX_P1, (HDMI_RX_MPEG_DATA00_REG + i), No parentheses. > + ®val); > + sp.tx_packet_mpeg.mpeg_data[i] = regval; > + } > +} > + > +static void sp_tx_mpeg_setup(struct anx78xx *anx78xx) > +{ > + u8 regval; > + int i; > + > + for (i = 0; i < 10; i++) { > + sp_read_reg(anx78xx, RX_P1, (HDMI_RX_MPEG_DATA00_REG + i), > + ®val); > + sp.tx_packet_mpeg.mpeg_data[i] = regval; > + } > +} > + > +static void sp_tx_auth_done_int_handler(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 bytebuf[2]; > + > + if (sp.hcdp_state > HDCP_HW_ENABLE && > + sp.tx_system_state == STATE_HDCP_AUTH) { > + sp_read_reg(anx78xx, TX_P0, SP_TX_HDCP_STATUS, bytebuf); > + if (bytebuf[0] & SP_TX_HDCP_AUTH_PASS) { > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x06, 0x80, 0x2a, 2, > + bytebuf); > + if ((bytebuf[1] & 0x08) || (bytebuf[0] & 0x80)) { > + dev_dbg(dev, "max cascade/devs exceeded!\n"); > + sp_tx_hdcp_encryption_disable(anx78xx); > + } else > + dev_dbg(dev, "%s\n", > + "Authentication pass in Auth_Done"); > + > + sp.hcdp_state = HDCP_FINISH; > + } else { > + dev_err(dev, "Authentication failed in AUTH_done\n"); > + sp_tx_video_mute(anx78xx, 1); > + sp_tx_clean_hdcp_status(anx78xx); > + sp.hcdp_state = HDCP_FAILED; > + } > + } > +} > + > +static void sp_tx_lt_done_int_handler(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 regval; > + > + if (sp.tx_lt_state == LT_WAITTING_FINISH && > + sp.tx_system_state == STATE_LINK_TRAINING) { > + sp_read_reg(anx78xx, TX_P0, LT_CTRL, ®val); > + if (regval & 0x70) { > + regval = (regval & 0x70) >> 4; > + dev_dbg(dev, "LT failed in interrupt, ERR = %.2x\n", > + regval); > + sp.tx_lt_state = LT_ERROR; > + } else { > + dev_dbg(dev, "lt_done: LT Finish\n"); > + sp.tx_lt_state = LT_FINISH; > + } > + } > +} > + > +static void sp_hdmi_rx_clk_det_int(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + > + dev_dbg(dev, "*HDMI_RX Interrupt: Pixel Clock Change.\n"); > + if (sp.tx_system_state > STATE_VIDEO_OUTPUT) { > + sp_tx_video_mute(anx78xx, 1); > + sp_tx_enable_audio_output(anx78xx, 0); > + sp_tx_set_sys_state(anx78xx, STATE_VIDEO_OUTPUT); > + } > +} > + > +static void sp_hdmi_rx_hdmi_dvi_int(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 regval; > + > + dev_dbg(dev, "sp_hdmi_rx_hdmi_dvi_int.\n"); > + sp_read_reg(anx78xx, RX_P0, HDMI_STATUS, ®val); > + sp.hdmi_dvi_status = 1; = 1? What does that mean? > + if ((regval & BIT(0)) != (sp.hdmi_dvi_status & BIT(0))) { != 1: you assigned that variable just above... > + dev_dbg(dev, "hdmi_dvi_int: Is HDMI MODE: %x.\n", > + regval & HDMI_MODE); > + sp.hdmi_dvi_status = regval & BIT(0); > + hdmi_rx_mute_audio(anx78xx, 1); > + system_state_change_with_case(anx78xx, STATE_LINK_TRAINING); > + } > +} > + > +static void sp_hdmi_rx_new_avi_int(struct anx78xx *anx78xx) > +{ > + sp_tx_lvttl_bit_mapping(anx78xx); > + sp_tx_set_colorspace(anx78xx); > + sp_tx_avi_setup(anx78xx); > + sp_tx_config_packets(anx78xx, AVI_PACKETS); > +} > + > +static void sp_hdmi_rx_new_vsi_int(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 hdmi_video_format, v3d_structure; > + > + sp_write_reg_and(anx78xx, TX_P0, SP_TX_3D_VSC_CTRL, > + ~INFO_FRAME_VSC_EN); > + > + /* VSI package header */ > + if ((sp_i2c_read_byte(anx78xx, RX_P1, > + HDMI_RX_MPEG_TYPE_REG) != 0x81) || > + (sp_i2c_read_byte(anx78xx, RX_P1, HDMI_RX_MPEG_VER_REG) != 0x01)) > + return; > + > + dev_dbg(dev, "Setup VSI package!\n"); > + > + sp_tx_vsi_setup(anx78xx); > + sp_tx_config_packets(anx78xx, VSI_PACKETS); > + > + sp_read_reg(anx78xx, RX_P1, HDMI_RX_MPEG_DATA03_REG, > + &hdmi_video_format); > + > + if ((hdmi_video_format & 0xe0) == 0x40) { > + dev_dbg(dev, "3D VSI packet detected. Config VSC packet\n"); > + > + sp_read_reg(anx78xx, RX_P1, HDMI_RX_MPEG_DATA05_REG, > + &v3d_structure); > + > + switch (v3d_structure & 0xf0) { > + case 0x00: > + v3d_structure = 0x02; > + break; > + case 0x20: > + v3d_structure = 0x03; > + break; > + case 0x30: > + v3d_structure = 0x04; > + break; > + default: > + v3d_structure = 0x00; > + dev_dbg(dev, "3D structure is not supported\n"); > + break; > + } > + sp_write_reg(anx78xx, TX_P0, SP_TX_VSC_DB1, v3d_structure); > + } > + sp_write_reg_or(anx78xx, TX_P0, SP_TX_3D_VSC_CTRL, INFO_FRAME_VSC_EN); > + sp_write_reg_and(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ~SPD_IF_EN); > + sp_write_reg_or(anx78xx, TX_P0, SP_TX_PKT_EN_REG, SPD_IF_UD); > + sp_write_reg_or(anx78xx, TX_P0, SP_TX_PKT_EN_REG, SPD_IF_EN); > +} > + > +static void sp_hdmi_rx_no_vsi_int(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 regval; > + > + sp_read_reg(anx78xx, TX_P0, SP_TX_3D_VSC_CTRL, ®val); > + if (regval & INFO_FRAME_VSC_EN) { > + dev_dbg(dev, "No new VSI is received, disable VSC packet\n"); > + regval &= ~INFO_FRAME_VSC_EN; > + sp_write_reg(anx78xx, TX_P0, SP_TX_3D_VSC_CTRL, regval); > + sp_tx_mpeg_setup(anx78xx); > + sp_tx_config_packets(anx78xx, MPEG_PACKETS); > + } > +} > + > +static inline void sp_hdmi_rx_restart_audio_chk(struct anx78xx *anx78xx) > +{ > + system_state_change_with_case(anx78xx, STATE_AUDIO_OUTPUT); > +} > + > +static void sp_hdmi_rx_cts_rcv_int(struct anx78xx *anx78xx) > +{ > + if (sp.tx_ao_state == AO_INIT) > + sp.tx_ao_state = AO_CTS_RCV_INT; > + else if (sp.tx_ao_state == AO_AUDIO_RCV_INT) > + sp.tx_ao_state = AO_RCV_INT_FINISH; > +} > + > +static void sp_hdmi_rx_audio_rcv_int(struct anx78xx *anx78xx) > +{ > + if (sp.tx_ao_state == AO_INIT) > + sp.tx_ao_state = AO_AUDIO_RCV_INT; > + else if (sp.tx_ao_state == AO_CTS_RCV_INT) > + sp.tx_ao_state = AO_RCV_INT_FINISH; > +} > + > +static void sp_hdmi_rx_audio_samplechg_int(struct anx78xx *anx78xx) > +{ > + u16 i; > + u8 regval; > + > + /* transfer audio chaneel status from HDMI Rx to Slinmport Tx */ channel? Slimport? > + for (i = 0; i < 5; i++) { > + sp_read_reg(anx78xx, RX_P0, HDMI_RX_AUD_IN_CH_STATUS1_REG + i, > + ®val); > + sp_write_reg(anx78xx, TX_P2, SP_TX_AUD_CH_STATUS_REG1 + i, > + regval); > + } > +} > + > +static void sp_hdmi_rx_hdcp_error_int(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + static u8 count; > + > + dev_dbg(dev, "*HDMI_RX Interrupt: hdcp error.\n"); > + if (count >= 40) { > + count = 0; > + dev_dbg(dev, "Lots of hdcp error occurred ...\n"); > + hdmi_rx_mute_audio(anx78xx, 1); > + hdmi_rx_mute_video(anx78xx, 1); > + hdmi_rx_set_hpd(anx78xx, 0); > + usleep_range(10000, 11000); > + hdmi_rx_set_hpd(anx78xx, 1); > + } else { > + count++; > + } > +} > + > +static void sp_hdmi_rx_new_gcp_int(struct anx78xx *anx78xx) > +{ > + u8 regval; > + > + sp_read_reg(anx78xx, RX_P1, HDMI_RX_GENERAL_CTRL, ®val); > + if (regval & SET_AVMUTE) { > + hdmi_rx_mute_video(anx78xx, 1); > + hdmi_rx_mute_audio(anx78xx, 1); > + } else if (regval & CLEAR_AVMUTE) { > + hdmi_rx_mute_video(anx78xx, 0); > + hdmi_rx_mute_audio(anx78xx, 0); > + } > +} > + > +static void sp_tx_hpd_int_handler(struct anx78xx *anx78xx, u8 hpd_source) I'd split this function in 2 parts, one for LOST, the other for CHANGE, since they do not share any code anyway. > +{ > + struct device *dev = &anx78xx->client->dev; > + > + switch (hpd_source) { > + case HPD_LOST: > + hdmi_rx_set_hpd(anx78xx, 0); > + sp_tx_set_sys_state(anx78xx, STATE_WAITTING_CABLE_PLUG); > + break; > + case HPD_CHANGE: > + dev_dbg(dev, "HPD:____________HPD changed!\n"); > + usleep_range(2000, 4000); > + if (sp.common_int_status.common_int[3] & HPD_IRQ) > + sp_hpd_irq_process(anx78xx); > + > + if (sp_i2c_read_byte(anx78xx, TX_P0, > + SP_TX_SYS_CTRL3_REG) & HPD_STATUS) { > + if (sp.common_int_status.common_int[3] & HPD_IRQ) > + sp_hpd_irq_process(anx78xx); > + } else { > + if (sp_i2c_read_byte(anx78xx, TX_P0, > + SP_TX_SYS_CTRL3_REG) & > + HPD_STATUS) { > + hdmi_rx_set_hpd(anx78xx, 0); > + sp_tx_set_sys_state(anx78xx, > + STATE_WAITTING_CABLE_PLUG); > + } > + } > + break; > + case PLUG: This case is never called. > + dev_dbg(dev, "HPD:____________HPD changed!\n"); > + if (sp.tx_system_state < STATE_SP_INITIALIZED) > + sp_tx_set_sys_state(anx78xx, STATE_SP_INITIALIZED); > + break; > + default: > + break; > + } > +} > + > +static void sp_system_isr_handler(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + > + if (sp.common_int_status.common_int[3] & HPD_CHANGE) > + sp_tx_hpd_int_handler(anx78xx, HPD_CHANGE); > + if (sp.common_int_status.common_int[3] & HPD_LOST) > + sp_tx_hpd_int_handler(anx78xx, HPD_LOST); > + if (sp.common_int_status.common_int[3] & HPD_IRQ) > + dev_dbg(dev, "++++++++++++++++========HDCP_IRQ interrupt\n"); > + if (sp.common_int_status.common_int[0] & PLL_LOCK_CHG) > + sp_tx_pll_changed_int_handler(anx78xx); > + > + if (sp.common_int_status.common_int[1] & HDCP_AUTH_DONE) > + sp_tx_auth_done_int_handler(anx78xx); > + > + if (sp.common_int_status.common_int[2] & HDCP_LINK_CHECK_FAIL) > + sp_tx_hdcp_link_chk_fail_handler(anx78xx); > + > + if (sp.common_int_status.common_int[4] & TRAINING_FINISH) > + sp_tx_lt_done_int_handler(anx78xx); > + > + if (sp.tx_system_state > STATE_SINK_CONNECTION) { > + if (sp.hdmi_rx_int_status.hdmi_rx_int[5] & NEW_AVI) > + sp_hdmi_rx_new_avi_int(anx78xx); > + } > + > + if (sp.tx_system_state > STATE_VIDEO_OUTPUT) { > + if (sp.hdmi_rx_int_status.hdmi_rx_int[6] & NEW_VS) { > + sp.hdmi_rx_int_status.hdmi_rx_int[6] &= ~NO_VSI; > + sp_hdmi_rx_new_vsi_int(anx78xx); > + } > + if (sp.hdmi_rx_int_status.hdmi_rx_int[6] & NO_VSI) > + sp_hdmi_rx_no_vsi_int(anx78xx); > + } > + > + if (sp.tx_system_state >= STATE_VIDEO_OUTPUT) { > + if (sp.hdmi_rx_int_status.hdmi_rx_int[0] & CKDT_CHANGE) > + sp_hdmi_rx_clk_det_int(anx78xx); > + > + if (sp.hdmi_rx_int_status.hdmi_rx_int[0] & SCDT_CHANGE) > + dev_dbg(dev, "*HDMI_RX Interrupt: Sync Detect.\n"); > + > + if (sp.hdmi_rx_int_status.hdmi_rx_int[0] & HDMI_DVI) > + sp_hdmi_rx_hdmi_dvi_int(anx78xx); > + > + if ((sp.hdmi_rx_int_status.hdmi_rx_int[5] & NEW_AUD) || > + (sp.hdmi_rx_int_status.hdmi_rx_int[2] & AUD_MODE_CHANGE)) > + sp_hdmi_rx_restart_audio_chk(anx78xx); > + > + if (sp.hdmi_rx_int_status.hdmi_rx_int[5] & CTS_RCV) > + sp_hdmi_rx_cts_rcv_int(anx78xx); > + > + if (sp.hdmi_rx_int_status.hdmi_rx_int[4] & AUDIO_RCV) > + sp_hdmi_rx_audio_rcv_int(anx78xx); > + > + if (sp.hdmi_rx_int_status.hdmi_rx_int[1] & HDCP_ERR) > + sp_hdmi_rx_hdcp_error_int(anx78xx); > + > + if (sp.hdmi_rx_int_status.hdmi_rx_int[5] & NEW_CP) > + sp_hdmi_rx_new_gcp_int(anx78xx); > + > + if (sp.hdmi_rx_int_status.hdmi_rx_int[1] & AUDIO_SAMPLE_CHANGE) > + sp_hdmi_rx_audio_samplechg_int(anx78xx); > + } > +} > + > +static void sp_tx_show_information(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 regval, regval1; > + u16 h_res, h_act, v_res, v_act; > + u16 h_fp, h_sw, h_bp, v_fp, v_sw, v_bp; > + unsigned long fresh_rate; > + unsigned long pclk; > + > + dev_dbg(dev, "\n************* SP Video Information **************\n"); > + > + switch (sp_tx_get_link_bw(anx78xx)) { > + case LINK_1P62G: > + dev_dbg(dev, "BW = 1.62G\n"); > + break; > + case LINK_2P7G: > + dev_dbg(dev, "BW = 2.7G\n"); > + break; > + case LINK_5P4G: > + dev_dbg(dev, "BW = 5.4G\n"); > + break; > + case LINK_6P75G: > + dev_dbg(dev, "BW = 6.75G\n"); > + break; > + default: > + break; > + } > + > + pclk = sp_tx_pclk_calc(anx78xx); > + pclk = pclk / 10; > + > + sp_read_reg(anx78xx, TX_P2, SP_TX_TOTAL_LINE_STA_L, ®val); > + sp_read_reg(anx78xx, TX_P2, SP_TX_TOTAL_LINE_STA_H, ®val1); > + > + v_res = regval1; > + v_res = v_res << 8; > + v_res = v_res + regval; > + > + sp_read_reg(anx78xx, TX_P2, SP_TX_ACT_LINE_STA_L, ®val); > + sp_read_reg(anx78xx, TX_P2, SP_TX_ACT_LINE_STA_H, ®val1); > + > + v_act = regval1; > + v_act = v_act << 8; > + v_act = v_act + regval; > + > + sp_read_reg(anx78xx, TX_P2, SP_TX_TOTAL_PIXEL_STA_L, ®val); > + sp_read_reg(anx78xx, TX_P2, SP_TX_TOTAL_PIXEL_STA_H, ®val1); > + > + h_res = regval1; > + h_res = h_res << 8; > + h_res = h_res + regval; > + > + sp_read_reg(anx78xx, TX_P2, SP_TX_ACT_PIXEL_STA_L, ®val); > + sp_read_reg(anx78xx, TX_P2, SP_TX_ACT_PIXEL_STA_H, ®val1); > + > + h_act = regval1; > + h_act = h_act << 8; > + h_act = h_act + regval; > + > + sp_read_reg(anx78xx, TX_P2, SP_TX_H_F_PORCH_STA_L, ®val); > + sp_read_reg(anx78xx, TX_P2, SP_TX_H_F_PORCH_STA_H, ®val1); > + > + h_fp = regval1; > + h_fp = h_fp << 8; > + h_fp = h_fp + regval; > + > + sp_read_reg(anx78xx, TX_P2, SP_TX_H_SYNC_STA_L, ®val); > + sp_read_reg(anx78xx, TX_P2, SP_TX_H_SYNC_STA_H, ®val1); > + > + h_sw = regval1; > + h_sw = h_sw << 8; > + h_sw = h_sw + regval; > + > + sp_read_reg(anx78xx, TX_P2, SP_TX_H_B_PORCH_STA_L, ®val); > + sp_read_reg(anx78xx, TX_P2, SP_TX_H_B_PORCH_STA_H, ®val1); > + > + h_bp = regval1; > + h_bp = h_bp << 8; > + h_bp = h_bp + regval; > + > + sp_read_reg(anx78xx, TX_P2, SP_TX_V_F_PORCH_STA, ®val); > + v_fp = regval; > + > + sp_read_reg(anx78xx, TX_P2, SP_TX_V_SYNC_STA, ®val); > + v_sw = regval; > + > + sp_read_reg(anx78xx, TX_P2, SP_TX_V_B_PORCH_STA, ®val); > + v_bp = regval; > + > + dev_dbg(dev, "Total resolution is %d * %d\n", h_res, v_res); > + > + dev_dbg(dev, "HF=%d, HSW=%d, HBP=%d\n", h_fp, h_sw, h_bp); > + dev_dbg(dev, "VF=%d, VSW=%d, VBP=%d\n", v_fp, v_sw, v_bp); > + dev_dbg(dev, "Active resolution is %d * %d", h_act, v_act); > + > + if (h_res == 0 || v_res == 0) { > + fresh_rate = 0; > + } else { > + fresh_rate = pclk * 1000; > + fresh_rate = fresh_rate / h_res; > + fresh_rate = fresh_rate * 1000; > + fresh_rate = fresh_rate / v_res; > + } > + dev_dbg(dev, " @ %ldHz\n", fresh_rate); > + > + sp_read_reg(anx78xx, TX_P0, SP_TX_VID_CTRL, ®val); > + > + if ((regval & 0x06) == 0x00) > + dev_dbg(dev, "ColorSpace: RGB,"); > + else if ((regval & 0x06) == 0x02) > + dev_dbg(dev, "ColorSpace: YCbCr422,"); > + else if ((regval & 0x06) == 0x04) > + dev_dbg(dev, "ColorSpace: YCbCr444,"); > + > + sp_read_reg(anx78xx, TX_P0, SP_TX_VID_CTRL, ®val); > + > + if ((regval & 0xe0) == 0x00) > + dev_dbg(dev, "6 BPC\n"); > + else if ((regval & 0xe0) == 0x20) > + dev_dbg(dev, "8 BPC\n"); > + else if ((regval & 0xe0) == 0x40) > + dev_dbg(dev, "10 BPC\n"); > + else if ((regval & 0xe0) == 0x60) > + dev_dbg(dev, "12 BPC\n"); > + > + if (is_anx_dongle(anx78xx)) { > + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x05, 0x23, 1, ®val); > + dev_dbg(dev, "Analogix Dongle FW Ver %.2x\n", regval & 0x7f); > + } > + > + dev_dbg(dev, "\n**************************************************\n"); > +} > + > +static void sp_clean_system_status(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + > + if (sp.need_clean_status) { > + dev_dbg(dev, "sp_clean_system_status. A -> B;\n"); > + dev_dbg(dev, "A:"); > + sp_print_system_state(anx78xx, sp.tx_system_state_bak); > + dev_dbg(dev, "B:"); > + sp_print_system_state(anx78xx, sp.tx_system_state); > + > + sp.need_clean_status = 0; > + if (sp.tx_system_state_bak >= STATE_LINK_TRAINING) { > + if (sp.tx_system_state >= STATE_AUDIO_OUTPUT) { > + hdmi_rx_mute_audio(anx78xx, 1); > + } else { > + hdmi_rx_mute_video(anx78xx, 1); > + sp_tx_video_mute(anx78xx, 1); > + } > + } > + if (sp.tx_system_state_bak >= STATE_HDCP_AUTH && > + sp.tx_system_state <= STATE_HDCP_AUTH) { > + if (sp_i2c_read_byte(anx78xx, TX_P0, TX_HDCP_CTRL0) > + & 0xfc) > + sp_tx_clean_hdcp_status(anx78xx); > + } > + > + if (sp.hcdp_state != HDCP_CAPABLE_CHECK) > + sp.hcdp_state = HDCP_CAPABLE_CHECK; > + > + if (sp.tx_sc_state != SC_INIT) > + sp.tx_sc_state = SC_INIT; > + if (sp.tx_lt_state != LT_INIT) > + sp.tx_lt_state = LT_INIT; > + if (sp.tx_vo_state != VO_WAIT_VIDEO_STABLE) > + sp.tx_vo_state = VO_WAIT_VIDEO_STABLE; > + } > +} > + > +/******************add for HDCP cap check********************/ > +static u8 sp_hdcp_cap_check(struct anx78xx *anx78xx) > +{ > + struct device *dev = &anx78xx->client->dev; > + u8 g_hdcp_cap = 0; > + u8 value; > + > + if (sp_tx_aux_dpcdread_bytes(anx78xx, 0x06, 0x80, 0x28, 1, &value) == 0) > + g_hdcp_cap = value & 0x01; > + else > + dev_dbg(dev, "HDCP CAPABLE: read AUX err!\n"); > + > + dev_dbg(dev, "hdcp cap check: %s Supported\n", > + g_hdcp_cap ? "" : "No"); > + > + return g_hdcp_cap; > +} > + > +/******************End HDCP cap check********************/ > + > +static void sp_tasks_handler(struct anx78xx *anx78xx) > +{ > + sp_system_isr_handler(anx78xx); > + sp_hdcp_external_ctrl_flag_monitor(anx78xx); > + sp_clean_system_status(anx78xx); > + /*clear up backup system state*/ > + if (sp.tx_system_state_bak != sp.tx_system_state) > + sp.tx_system_state_bak = sp.tx_system_state; > +} > + > +/******************End task process********************/ > + > +void sp_main_process(struct anx78xx *anx78xx) > +{ > + sp_state_process(anx78xx); > + if (sp.tx_system_state > STATE_WAITTING_CABLE_PLUG) { > + sp_int_rec(anx78xx); > + sp_tasks_handler(anx78xx); > + } > +} > diff --git a/drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.h b/drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.h > new file mode 100644 > index 0000000..04dbe06 > --- /dev/null > +++ b/drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.h > @@ -0,0 +1,214 @@ > +/* > + * Copyright(c) 2015, Analogix Semiconductor. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 and > + * only version 2 as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + */ > + > +#ifndef __SLIMPORT_TX_DRV_H > +#define __SLIMPORT_TX_DRV_H > + > +#include "anx78xx.h" > +#include "slimport_tx_reg.h" > + > +#define FW_VERSION 0x22 > + > +#define DVI_MODE 0x00 > +#define HDMI_MODE 0x01 > + > +#define SP_POWER_ON 1 > +#define SP_POWER_DOWN 0 > + > +#define MAX_BUF_CNT 16 > + > +#define SP_BREAK(current_status, next_status) \ > + { if (next_status != (current_status) + 1) break; } Please remove these control flow macros... > + > +enum rx_cbl_type { > + DWN_STRM_IS_NULL, DOWN_STREAM_IS_NULL > + DWN_STRM_IS_HDMI, > + DWN_STRM_IS_DIGITAL, > + DWN_STRM_IS_ANALOG, > + DWN_STRM_NUM > +}; > + > +enum sp_tx_state { > + STATE_WAITTING_CABLE_PLUG, > + STATE_SP_INITIALIZED, > + STATE_SINK_CONNECTION, > + STATE_PARSE_EDID, > + STATE_LINK_TRAINING, > + STATE_VIDEO_OUTPUT, > + STATE_HDCP_AUTH, > + STATE_AUDIO_OUTPUT, > + STATE_PLAY_BACK > +}; > + > +enum sp_tx_power_block { > + SP_TX_PWR_REG = REGISTER_PD, > + SP_TX_PWR_HDCP = HDCP_PD, > + SP_TX_PWR_AUDIO = AUDIO_PD, > + SP_TX_PWR_VIDEO = VIDEO_PD, > + SP_TX_PWR_LINK = LINK_PD, > + SP_TX_PWR_TOTAL = TOTAL_PD, > + SP_TX_PWR_NUMS > +}; > + > +enum hdmi_color_depth { > + HDMI_LEGACY = 0x00, > + HDMI_24BIT = 0x04, > + HDMI_30BIT = 0x05, > + HDMI_36BIT = 0x06, > + HDMI_48BIT = 0x07, > +}; > + > +enum sp_tx_send_msg { > + MSG_OCM_EN, > + MSG_INPUT_HDMI, > + MSG_INPUT_DVI, > + MSG_CLEAR_IRQ, > +}; > + > +enum sink_connection_status { > + SC_INIT, > + SC_CHECK_CABLE_TYPE, > + SC_WAITTING_CABLE_TYPE = SC_CHECK_CABLE_TYPE + 5, > + SC_SINK_CONNECTED, > + SC_NOT_CABLE, > + SC_STATE_NUM > +}; > + > +enum cable_type_status { > + CHECK_AUXCH, > + GETTED_CABLE_TYPE, > + CABLE_TYPE_STATE_NUM > +}; > + > +enum sp_tx_lt_status { > + LT_INIT, > + LT_WAIT_PLL_LOCK, > + LT_CHECK_LINK_BW, > + LT_START, > + LT_WAITTING_FINISH, > + LT_ERROR, > + LT_FINISH, > + LT_END, > + LT_STATES_NUM > +}; > + > +enum hdcp_status { > + HDCP_CAPABLE_CHECK, > + HDCP_WAITTING_VID_STB, > + HDCP_HW_ENABLE, > + HDCP_WAITTING_FINISH, > + HDCP_FINISH, > + HDCP_FAILED, > + HDCP_NOT_SUPPORT, > + HDCP_PROCESS_STATE_NUM > +}; > + > +enum video_output_status { > + VO_WAIT_VIDEO_STABLE, > + VO_WAIT_TX_VIDEO_STABLE, > + VO_CHECK_VIDEO_INFO, > + VO_FINISH, > + VO_STATE_NUM > +}; > + > +enum audio_output_status { > + AO_INIT, > + AO_CTS_RCV_INT, > + AO_AUDIO_RCV_INT, > + AO_RCV_INT_FINISH, > + AO_OUTPUT, > + AO_STATE_NUM > +}; > + > +struct packet_avi { > + u8 avi_data[13]; > +}; > + > +struct packet_spd { > + u8 spd_data[25]; > +}; > + > +struct packet_mpeg { > + u8 mpeg_data[13]; > +}; > + > +struct audio_info_frame { > + u8 type; > + u8 version; > + u8 length; > + u8 pb_byte[11]; > +}; > + > +enum packets_type { > + AVI_PACKETS, > + SPD_PACKETS, > + MPEG_PACKETS, > + VSI_PACKETS, > + AUDIF_PACKETS > +}; > + > +struct common_int { > + u8 common_int[5]; > + u8 change_flag; > +}; > + > +struct hdmi_rx_int { > + u8 hdmi_rx_int[7]; > + u8 change_flag; > +}; > + > +enum xtal_enum { > + XTAL_19D2M, > + XTAL_24M, > + XTAL_25M, > + XTAL_26M, > + XTAL_27M, > + XTAL_38D4M, > + XTAL_52M, > + XTAL_NOT_SUPPORT, > + XTAL_CLK_NUM > +}; > + > +enum sp_ssc_dep { > + SSC_DEP_DISABLE = 0x0, > + SSC_DEP_500PPM, > + SSC_DEP_1000PPM, > + SSC_DEP_1500PPM, > + SSC_DEP_2000PPM, > + SSC_DEP_2500PPM, > + SSC_DEP_3000PPM, > + SSC_DEP_3500PPM, > + SSC_DEP_4000PPM, > + SSC_DEP_4500PPM, > + SSC_DEP_5000PPM, > + SSC_DEP_5500PPM, > + SSC_DEP_6000PPM > +}; > + > +struct anx78xx_clock_data { > + unsigned char xtal_clk; > + unsigned int xtal_clk_m10; > +}; > + > +bool sp_chip_detect(struct anx78xx *anx78xx); > + > +void sp_main_process(struct anx78xx *anx78xx); > + > +void sp_tx_variable_init(void); > + > +enum sp_tx_state sp_tx_current_state(void); > + > +void sp_tx_clean_state_machine(void); > + > +#endif > diff --git a/drivers/gpu/drm/bridge/anx78xx/slimport_tx_reg.h b/drivers/gpu/drm/bridge/anx78xx/slimport_tx_reg.h > new file mode 100644 > index 0000000..56b575c > --- /dev/null > +++ b/drivers/gpu/drm/bridge/anx78xx/slimport_tx_reg.h > @@ -0,0 +1,807 @@ > +/* > + * Copyright(c) 2015, Analogix Semiconductor. All rights reserved. > + * > + * This program is free software; you can redistribute it and/or modify > + * it under the terms of the GNU General Public License version 2 and > + * only version 2 as published by the Free Software Foundation. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + * > + */ > + > +#ifndef __SLIMPORT_TX_REG_DEF_H > +#define __SLIMPORT_TX_REG_DEF_H > + > +#define TX_P0 0x70 > +#define TX_P1 0x7a > +#define TX_P2 0x72 > + > +#define RX_P0 0x7e > +#define RX_P1 0x80 > + > +/***************************************************************/ > +/* Register definition of device address 0x7e */ > +/***************************************************************/ > + > +#define HDMI_RX_PORT_SEL_REG 0x10 > +#define DDC_EN 0x10 > +#define TMDS_EN 0x01 > + > +#define RX_SRST 0x11 > +#define VIDEO_RST 0x10 > +#define HDCP_MAN_RST 0x04 > +#define TMDS_RST 0x02 > +#define SW_MAN_RST 0x01 > + > +#define RX_SW_RST2 0x12 > +#define DDC_RST 0x04 > + > +#define HDMI_RX_SYS_STATUS_REG 0x14 > +#define PWR5V 0x08 > +#define TMDS_VSYNC_DET 0x04 > +#define TMDS_CLOCK_DET 0x02 > +#define TMDS_DE_DET 0x01 > + > +#define HDMI_STATUS 0x15 > +#define DEEP_COLOR_MODE 0x40 > +#define HDMI_AUD_LAYOUT 0x08 > +#define MUTE_STAT 0x04 > + > +#define RX_MUTE_CTRL 0x16 > +#define MUTE_POL 0x04 > +#define AUD_MUTE 0x02 > +#define VID_MUTE 0x01 > + > +#define HDMI_RX_SYS_CTRL1_REG 0x17 > + > +#define RX_SYS_PWDN1 0x18 > +#define PWDN_CTRL 0x01 > + > +#define RX_AEC_CTRL 0x20 > +#define AVC_OE 0x80 > +#define AAC_OE 0x40 > +#define AVC_EN 0x02 > +#define AAC_EN 0x01 > + > +#define RX_AEC_EN0 0x24 > +#define AEC_EN07 0x80 > +#define AEC_EN06 0x40 > +#define AEC_EN05 0x20 > +#define AEC_EN04 0x10 > +#define AEC_EN03 0x08 > +#define AEC_EN02 0x04 > +#define AEC_EN01 0x02 > +#define AEC_EN00 0x01 > + > +#define RX_AEC_EN1 0x25 > +#define AEC_EN15 0x80 > +#define AEC_EN14 0x40 > +#define AEC_EN13 0x20 > +#define AEC_EN12 0x10 > +#define AEC_EN11 0x08 > +#define AEC_EN10 0x04 > +#define AEC_EN09 0x02 > +#define AEC_EN08 0x01 > + > +#define RX_AEC_EN2 0x26 > +#define AEC_EN23 0x80 > +#define AEC_EN22 0x40 > +#define AEC_EN21 0x20 > +#define AEC_EN20 0x10 > +#define AEC_EN19 0x08 > +#define AEC_EN18 0x04 > +#define AEC_EN17 0x02 > +#define AEC_EN16 0x01 > + > +#define HDMI_RX_INT_STATUS1_REG 0x31 > +#define HDMI_DVI 0x80 > +#define CKDT_CHANGE 0x40 > +#define SCDT_CHANGE 0x20 > +#define PCLK_CHANGE 0x10 > +#define PLL_UNLOCK 0x08 > +#define CABLE_UNPLUG 0x04 > +#define SET_MUTE 0x02 > +#define SW_INTR 0x01 > + > +#define HDMI_RX_INT_STATUS2_REG 0x32 > +#define AUTH_START 0x80 > +#define AUTH_DONE 0x40 > +#define HDCP_ERR 0x20 > +#define ECC_ERR 0x10 > +#define AUDIO_SAMPLE_CHANGE 0x01 > + > +#define HDMI_RX_INT_STATUS3_REG 0x33 > +#define AUD_MODE_CHANGE 0x01 > + > +#define HDMI_RX_INT_STATUS4_REG 0x34 > +#define VSYNC_DET 0x80 > +#define SYNC_POL_CHANGE 0x40 > +#define V_RES_CHANGE 0x20 > +#define H_RES_CHANGE 0x10 > +#define I_P_CHANGE 0x08 > +#define DP_CHANGE 0x04 > +#define COLOR_DEPTH_CHANGE 0x02 > +#define COLOR_MODE_CHANGE 0x01 > + > +#define HDMI_RX_INT_STATUS5_REG 0x35 > +#define VFIFO_OVERFLOW 0x80 > +#define VFIFO_UNDERFLOW 0x40 > +#define CTS_N_ERR 0x08 > +#define NO_AVI 0x02 > +#define AUDIO_RCV 0x01 > + > +#define HDMI_RX_INT_STATUS6_REG 0x36 > +#define CTS_RCV 0x80 > +#define NEW_UNR_PKT 0x40 > +#define NEW_MPEG 0x20 > +#define NEW_AUD 0x10 > +#define NEW_SPD 0x08 > +#define NEW_ACP 0x04 > +#define NEW_AVI 0x02 > +#define NEW_CP 0x01 > + > +#define HDMI_RX_INT_STATUS7_REG 0x37 > +#define NO_VSI 0x80 > +#define HSYNC_DET 0x20 > +#define NEW_VS 0x10 > +#define NO_ACP 0x08 > +#define REF_CLK_CHG 0x04 > +#define CEC_RX_READY 0x02 > +#define CEC_TX_DONE 0x01 > + > +#define HDMI_RX_PKT_RX_INDU_INT_CTRL 0x3f > +#define NEW_VS_CTRL 0x80 > +#define NEW_UNR 0x40 > +#define NEW_MPEG 0x20 > +#define NEW_AUD 0x10 > +#define NEW_SPD 0x08 > +#define NEW_ACP 0x04 > +#define NEW_AVI 0x02 > + > +#define HDMI_RX_INT_MASK1_REG 0x41 > +#define HDMI_RX_INT_MASK2_REG 0x42 > +#define HDMI_RX_INT_MASK3_REG 0x43 > +#define HDMI_RX_INT_MASK4_REG 0x44 > +#define HDMI_RX_INT_MASK5_REG 0x45 > +#define HDMI_RX_INT_MASK6_REG 0x46 > +#define HDMI_RX_INT_MASK7_REG 0x47 > + > +#define HDMI_RX_TMDS_CTRL_REG1 0x50 > +#define HDMI_RX_TMDS_CTRL_REG2 0x51 > +#define HDMI_RX_TMDS_CTRL_REG4 0x53 > +#define HDMI_RX_TMDS_CTRL_REG5 0x54 > +#define HDMI_RX_TMDS_CTRL_REG6 0x55 > +#define HDMI_RX_TMDS_CTRL_REG7 0x56 > +#define TERM_PD 0x01 > + > +#define HDMI_RX_TMDS_CTRL_REG18 0x61 > +#define PLL_RESET 0x10 > + > +#define HDMI_RX_TMDS_CTRL_REG19 0x62 > +#define HDMI_RX_TMDS_CTRL_REG20 0x63 > +#define HDMI_RX_TMDS_CTRL_REG21 0x64 > +#define HDMI_RX_TMDS_CTRL_REG22 0x65 > + > +#define HDMI_RX_VIDEO_STATUS_REG1 0x70 > +#define COLOR_DEPTH 0xf0 > +#define DEFAULT_PHASE 0x08 > +#define VIDEO_TYPE 0x04 > + > +#define HDMI_RX_HTOTAL_LOW 0x71 > +#define HDMI_RX_HTOTAL_HIGH 0x72 > +#define HDMI_RX_VTOTAL_LOW 0x73 > +#define HDMI_RX_VTOTAL_HIGH 0x74 > + > +#define HDMI_RX_HACT_LOW 0x75 > +#define HDMI_RX_HACT_HIGH 0x76 > +#define HDMI_RX_VACT_LOW 0x77 > +#define HDMI_RX_VACT_HIGH 0x78 > + > +#define HDMI_RX_V_SYNC_WIDTH 0x79 > +#define HDMI_RX_V_BACK_PORCH 0x7a > +#define HDMI_RX_H_FRONT_PORCH_LOW 0x7b > +#define HDMI_RX_H_FRONT_PORCH_HIGH 0x7c > + > +#define HDMI_RX_H_SYNC_WIDTH_LOW 0x7d > +#define HDMI_RX_H_SYNC_WIDTH_HIGH 0x7e > + > +#define RX_VID_DATA_RNG 0x83 > +#define YC_LIMT 0x10 > +#define OUTPUT_LIMIT_EN 0x08 > +#define OUTPUT_LIMIT_RANGE 0x04 > +#define R2Y_INPUT_LIMIT 0x02 > +#define XVYCC_LIMIT 0x01 > + > +#define HDMI_RX_VID_OUTPUT_CTRL3_REG 0x86 > + > +#define HDMI_RX_VID_PCLK_CNTR_REG 0x8b > + > +/* Pixel Clock High Resolution Counter Register 1 */ > +#define PCLK_HR_CNT1 0x8c > +/* Pixel Clock High Resolution Counter Register 2 */ > +#define PCLK_HR_CNT2 0x8d > + > +#define HDMI_RX_AUD_IN_CH_STATUS1_REG 0xc7 > + > +/* Audio in S/PDIF Channel Status Register 4 */ > +#define AUD_SPDIF_CHST4 0xca > +#define FS_FREQ_44100HZ 0x00 > +#define FS_FREQ_48000HZ 0x02 > +#define FS_FREQ_32000HZ 0x03 > +#define FS_FREQ_88200HZ 0x08 > +#define FS_FREQ_96000HZ 0x0a > +#define FS_FREQ_176400HZ 0x0c > +#define FS_FREQ_192000HZ 0x0e > + > +#define RX_CEC_CTRL 0xd0 > +#define CEC_RX_EN 0x08 > +#define CEC_TX_ST 0x04 > +#define CEC_PIN_SEL 0x02 > +#define CEC_RST 0x01 > + > +#define HDMI_RX_CEC_RX_STATUS_REG 0xd1 > +#define HDMI_RX_CEC_RX_BUSY 0x80 > +#define HDMI_RX_CEC_RX_FULL 0x20 > +#define HDMI_RX_CEC_RX_EMP 0x10 > + > +#define HDMI_RX_CEC_TX_STATUS_REG 0xd2 > +#define HDMI_RX_CEC_TX_BUSY 0x80 > +#define HDMI_RX_CEC_TX_FAIL 0x40 > +#define HDMI_RX_CEC_TX_FULL 0x20 > +#define HDMI_RX_CEC_TX_EMP 0x10 > + > +#define HDMI_RX_CEC_FIFO_REG 0xd3 > + > +#define RX_CEC_SPEED 0xd4 > +#define CEC_SPEED_27M 0x40 > + > +#define HDMI_RX_HDMI_CRITERIA_REG 0xe1 > + > +#define HDMI_RX_HDCP_EN_CRITERIA_REG 0xe2 > +#define ENC_EN_MODE 0x20 > + > +#define RX_CHIP_CTRL 0xe3 > +#define MAN_HDMI5V_DET 0x08 > +#define PLLLOCK_CKDT_EN 0x04 > +#define ANALOG_CKDT_EN 0x02 > +#define DIGITAL_CKDT_EN 0x01 > + > +#define RX_PACKET_REV_STA 0xf3 > +#define AVI_RCVD 0x40 > +#define VSI_RCVD 0x20 > + > +/***************************************************************/ > +/* Register definition of device address 0x80 */ > +/***************************************************************/ > + > +#define HDMI_RX_HDCP_STATUS_REG 0x3f > +#define ADV_CIPHER 0x80 > +#define LOAD_KEY_DONE 0x40 > +#define DECRYPT_EN 0x20 > +#define AUTH_EN 0x10 > +#define BKSV_DISABLE 0x02 > +#define CLEAR_RI 0x01 > + > +#define HDMI_RX_SPD_TYPE_REG 0x40 > +#define HDMI_RX_SPD_VER_REG 0x41 > +#define HDMI_RX_SPD_LEN_REG 0x42 > +#define HDMI_RX_SPD_CHKSUM_REG 0x43 > +#define HDMI_RX_SPD_DATA00_REG 0x44 > + > +#define HDMI_RX_ACP_HB0_REG 0x60 > +#define HDMI_RX_ACP_HB1_REG 0x61 > +#define HDMI_RX_ACP_HB2_REG 0x62 > +#define HDMI_RX_ACP_DATA00_REG 0x63 > + > +#define HDMI_RX_AVI_TYPE_REG 0xa0 > +#define HDMI_RX_AVI_VER_REG 0xa1 > +#define HDMI_RX_AVI_LEN_REG 0xa2 > +#define HDMI_RX_AVI_CHKSUM_REG 0xa3 > +#define HDMI_RX_AVI_DATA00_REG 0xa4 > + > +#define HDMI_RX_AUDIO_TYPE_REG 0xc0 > +#define HDMI_RX_AUDIO_VER_REG 0xc1 > +#define HDMI_RX_AUDIO_LEN_REG 0xc2 > +#define HDMI_RX_AUDIO_CHKSUM_REG 0xc3 > +#define HDMI_RX_AUDIO_DATA00_REG 0xc4 > + > +#define HDMI_RX_MPEG_TYPE_REG 0xe0 > +#define HDMI_RX_MPEG_VER_REG 0xe1 > +#define HDMI_RX_MPEG_LEN_REG 0xe2 > +#define HDMI_RX_MPEG_CHKSUM_REG 0xe3 > +#define HDMI_RX_MPEG_DATA00_REG 0xe4 > +#define HDMI_RX_MPEG_DATA03_REG 0xe7 > +#define HDMI_RX_MPEG_DATA05_REG 0xe9 > + > +#define HDMI_RX_SPD_INFO_CTRL 0x5f > +#define HDMI_RX_ACP_INFO_CTRL 0x7f > + > +#define HDMI_RX_GENERAL_CTRL 0x9f > +#define CLEAR_AVMUTE 0x10 > +#define SET_AVMUTE 0x01 > + > +#define HDMI_RX_MPEG_VS_CTRL 0xdf > +#define HDMI_RX_MPEG_VS_INFO_CTRL 0xff > + > +/***************************************************************/ > +/* Register definition of device address 0x70 */ > +/***************************************************************/ > + > +#define SP_TX_HDCP_STATUS 0x00 > +#define SP_TX_HDCP_AUTH_PASS 0x02 > + > +#define TX_HDCP_CTRL0 0x01 > +#define STORE_AN 0x80 > +#define RX_REPEATER 0x40 > +#define RE_AUTH 0x20 > +#define SW_AUTH_OK 0x10 > +#define HARD_AUTH_EN 0x08 > +#define ENC_EN 0x04 > +#define BKSV_SRM_PASS 0x02 > +#define KSVLIST_VLD 0x01 > + > +#define SP_TX_HDCP_CTRL1_REG 0x02 > +#define AINFO_EN 0x04 > +#define RCV_11_EN 0x02 > +#define HDCP_11_EN 0x01 > + > +#define SP_TX_HDCP_LINK_CHK_FRAME_NUM 0x03 > +#define SP_TX_HDCP_CTRL2_REG 0x04 > + > +#define SP_TX_VID_BLANK_SET1 0x2c > +#define SP_TX_VID_BLANK_SET2 0x2d > +#define SP_TX_VID_BLANK_SET3 0x2e > + > +#define SP_TX_WAIT_R0_TIME 0x40 > +#define SP_TX_LINK_CHK_TIMER 0x41 > +#define SP_TX_WAIT_KSVR_TIME 0x42 > + > +#define HDCP_KEY_STATUS 0x5e > + > +#define M_VID_0 0xc0 > +#define M_VID_1 0xc1 > +#define M_VID_2 0xc2 > +#define N_VID_0 0xc3 > +#define N_VID_1 0xc4 > +#define N_VID_2 0xc5 > +#define HDCP_AUTO_TIMER 0x51 > +#define HDCP_AUTO_TIMER_VAL 0x00 > + > +#define HDCP_KEY_CMD 0x5f > +#define DISABLE_SYNC_HDCP 0x04 > + > +#define OTP_KEY_PROTECT1 0x60 > +#define OTP_KEY_PROTECT2 0x61 > +#define OTP_KEY_PROTECT3 0x62 > +#define OTP_PSW1 0xa2 > +#define OTP_PSW2 0x7e > +#define OTP_PSW3 0xc6 > + > +#define SP_TX_SYS_CTRL1_REG 0x80 > +#define CHIP_AUTH_RESET 0x80 > +#define PD_BYPASS_CHIP_AUTH 0x40 > +#define DET_STA 0x04 > +#define FORCE_DET 0x02 > +#define DET_CTRL 0x01 > + > +#define SP_TX_SYS_CTRL2_REG 0x81 > +#define CHA_STA 0x04 > +#define FORCE_CHA 0x02 > +#define CHA_CTRL 0x01 > + > +#define SP_TX_SYS_CTRL3_REG 0x82 > +#define HPD_STATUS 0x40 > +#define F_HPD 0x20 > +#define HPD_CTRL 0x10 > +#define STRM_VALID 0x04 > +#define F_VALID 0x02 > +#define VALID_CTRL 0x01 > + > +#define SP_TX_SYS_CTRL4_REG 0x83 > +#define ENHANCED_MODE 0x08 > + > +#define SP_TX_VID_CTRL 0x84 > + > +#define SP_TX_AUD_CTRL 0x87 > +#define AUD_EN 0x01 > + > +#define I2C_GEN_10US_TIMER0 0x88 > +#define I2C_GEN_10US_TIMER1 0x89 > + > +#define SP_TX_PKT_EN_REG 0x90 > +#define AUD_IF_UP 0x80 > +#define AVI_IF_UD 0x40 > +#define MPEG_IF_UD 0x20 > +#define SPD_IF_UD 0x10 > +#define AUD_IF_EN 0x08 > +#define AVI_IF_EN 0x04 > +#define MPEG_IF_EN 0x02 > +#define SPD_IF_EN 0x01 > + > +#define TX_HDCP_CTRL 0x92 > +#define AUTO_EN 0x80 > +#define AUTO_START 0x20 > +#define LINK_POLLING 0x02 > + > +#define SP_TX_LINK_BW_SET_REG 0xa0 > +#define LINK_BW_SET_MASK 0x0f > +#define LINK_6P75G 0x19 > +#define LINK_5P4G 0x14 > +#define LINK_2P7G 0x0a > +#define LINK_1P62G 0x06 > + > +#define SP_TX_TRAINING_PTN_SET_REG 0xa2 > +#define SCRAMBLE_DISABLE 0x20 > + > +#define SP_TX_LT_SET_REG 0xa3 > +#define TX_SW_SET_MASK 0x1b > +#define MAX_PRE_REACH 0x20 > +#define MAX_DRIVE_REACH 0x04 > +#define DRVIE_CURRENT_LEVEL1 0x01 > +#define PRE_EMP_LEVEL1 0x08 > + > +#define LT_CTRL 0xa8 > +#define SP_TX_LT_EN 0x01 > + > +#define ADDR_DP_CEP_TRAINING_CTRL0 0xa9 > +#define ADDR_DP_CEP_TRAINING_CTRL1 0xaa > +#define ADDR_DP_CEP_TRAINING_CTRL2 0xab > + > +#define TX_DEBUG1 0xb0 > +#define FORCE_HPD 0x80 > +#define HPD_POLLING_DET 0x40 > +#define HPD_POLLING_EN 0x20 > +#define DEBUG_PLL_LOCK 0x10 > +#define FORCE_PLL_LOCK 0x08 > +#define POLLING_EN 0x02 > + > +#define SP_TX_DP_POLLING_PERIOD 0xb3 > + > +#define TX_DP_POLLING 0xb4 > +#define AUTO_POLLING_DISABLE 0x01 > + > +#define TX_LINK_DEBUG 0xb8 > +#define M_VID_DEBUG 0x20 > +#define NEW_PRBS7 0x10 > +#define INSERT_ER 0x02 > +#define PRBS31_EN 0x01 > + > +#define DPCD_200 0xb9 > +#define DPCD_201 0xba > +#define DPCD_202 0xbb > +#define DPCD_203 0xbc > +#define DPCD_204 0xbd > +#define DPCD_205 0xbe > + > +#define SP_TX_PLL_CTRL_REG 0xc7 > +#define PLL_RST 0x40 > + > +#define SP_TX_ANALOG_PD_REG 0xc8 > +#define MACRO_PD 0x20 > +#define AUX_PD 0x10 > +#define CH0_PD 0x01 > + > +#define TX_MISC 0xcd > +#define EQ_TRAINING_LOOP 0x40 > + > +#define SP_TX_DOWN_SPREADING_CTRL1 0xd0 > +#define SP_TX_SSC_DISABLE 0xc0 > +#define SP_TX_SSC_DWSPREAD 0x40 > + > +#define SP_TX_M_CALCU_CTRL 0xd9 > +#define M_GEN_CLK_SEL 0x01 > + > +#define TX_EXTRA_ADDR 0xce > +#define I2C_STRETCH_DISABLE 0x80 > +#define I2C_EXTRA_ADDR 0x50 > + > +#define SP_TX_AUX_STATUS 0xe0 > +#define AUX_BUSY 0x10 > + > +#define AUX_DEFER_CTRL 0xe2 > +#define BUF_DATA_COUNT 0xe4 > + > +#define AUX_CTRL 0xe5 > +#define AUX_ADDR_7_0 0xe6 > +#define AUX_ADDR_15_8 0xe7 > +#define AUX_ADDR_19_16 0xe8 > + > +#define AUX_CTRL2 0xe9 > +#define ADDR_ONLY_BIT 0x02 > +#define AUX_OP_EN 0x01 > + > +#define SP_TX_3D_VSC_CTRL 0xea > +#define INFO_FRAME_VSC_EN 0x01 > + > +#define SP_TX_VSC_DB1 0xeb > + > +#define BUF_DATA_0 0xf0 > + > +/***************************************************************/ > +/* Register definition of device address 0x72 */ > +/***************************************************************/ > + > +#define SP_TX_VND_IDL_REG 0x00 > +#define SP_TX_VND_IDH_REG 0x01 > +#define SP_TX_DEV_IDL_REG 0x02 > +#define SP_TX_DEV_IDH_REG 0x03 > +#define SP_TX_DEV_REV_REG 0x04 > + > +#define SP_POWERD_CTRL_REG 0x05 > +#define REGISTER_PD 0x80 > +#define HDCP_PD 0x20 > +#define AUDIO_PD 0x10 > +#define VIDEO_PD 0x08 > +#define LINK_PD 0x04 > +#define TOTAL_PD 0x02 > + > +#define SP_TX_RST_CTRL_REG 0x06 > +#define MISC_RST 0x80 > +#define VIDCAP_RST 0x40 > +#define VIDFIF_RST 0x20 > +#define AUDFIF_RST 0x10 > +#define AUDCAP_RST 0x08 > +#define HDCP_RST 0x04 > +#define SW_RST 0x02 > +#define HW_RST 0x01 > + > +#define RST_CTRL2 0x07 > +#define AUX_RST 0x04 > +#define SERDES_FIFO_RST 0x02 > +#define I2C_REG_RST 0x01 > + > +#define VID_CTRL1 0x08 > +#define VIDEO_EN 0x80 > +#define VIDEO_MUTE 0x40 > +#define IN_BIT_SEL 0x04 > +#define DDR_CTRL 0x02 > +#define EDGE_CTRL 0x01 > + > +#define SP_TX_VID_CTRL2_REG 0x09 > +#define IN_BPC_12BIT 0x30 > +#define IN_BPC_10BIT 0x20 > +#define IN_BPC_8BIT 0x10 > + > +#define SP_TX_VID_CTRL3_REG 0x0a > +#define HPD_OUT 0x40 > + > +#define SP_TX_VID_CTRL5_REG 0x0c > +#define CSC_STD_SEL 0x80 > +#define RANGE_Y2R 0x20 > +#define CSPACE_Y2R 0x10 > + > +#define SP_TX_VID_CTRL6_REG 0x0d > +#define VIDEO_PROCESS_EN 0x40 > +#define UP_SAMPLE 0x02 > +#define DOWN_SAMPLE 0x01 > + > +#define SP_TX_VID_CTRL8_REG 0x0f > +#define VID_VRES_TH 0x01 > + > +#define SP_TX_TOTAL_LINE_STA_L 0x24 > +#define SP_TX_TOTAL_LINE_STA_H 0x25 > +#define SP_TX_ACT_LINE_STA_L 0x26 > +#define SP_TX_ACT_LINE_STA_H 0x27 > +#define SP_TX_V_F_PORCH_STA 0x28 > +#define SP_TX_V_SYNC_STA 0x29 > +#define SP_TX_V_B_PORCH_STA 0x2a > +#define SP_TX_TOTAL_PIXEL_STA_L 0x2b > +#define SP_TX_TOTAL_PIXEL_STA_H 0x2c > +#define SP_TX_ACT_PIXEL_STA_L 0x2d > +#define SP_TX_ACT_PIXEL_STA_H 0x2e > +#define SP_TX_H_F_PORCH_STA_L 0x2f > +#define SP_TX_H_F_PORCH_STA_H 0x30 > +#define SP_TX_H_SYNC_STA_L 0x31 > +#define SP_TX_H_SYNC_STA_H 0x32 > +#define SP_TX_H_B_PORCH_STA_L 0x33 > +#define SP_TX_H_B_PORCH_STA_H 0x34 > + > +#define SP_TX_DP_ADDR_REG1 0x3e > + > +#define SP_TX_VID_BIT_CTRL0_REG 0x40 > +#define SP_TX_VID_BIT_CTRL10_REG 0x4a > +#define SP_TX_VID_BIT_CTRL20_REG 0x54 > + > +#define SP_TX_AVI_TYPE 0x70 > +#define SP_TX_AVI_VER 0x71 > +#define SP_TX_AVI_LEN 0x72 > +#define SP_TX_AVI_DB0 0x73 > + > +#define BIT_CTRL_SPECIFIC 0x80 > +#define ENABLE_BIT_CTRL 0x01 > + > +#define SP_TX_AUD_TYPE 0x83 > +#define SP_TX_AUD_VER 0x84 > +#define SP_TX_AUD_LEN 0x85 > +#define SP_TX_AUD_DB0 0x86 > + > +#define SP_TX_SPD_TYPE 0x91 > +#define SP_TX_SPD_VER 0x92 > +#define SP_TX_SPD_LEN 0x93 > +#define SP_TX_SPD_DB0 0x94 > + > +#define SP_TX_MPEG_TYPE 0xb0 > +#define SP_TX_MPEG_VER 0xb1 > +#define SP_TX_MPEG_LEN 0xb2 > +#define SP_TX_MPEG_DB0 0xb3 > + > +#define SP_TX_AUD_CH_STATUS_REG1 0xd0 > + > +#define SP_TX_AUD_CH_NUM_REG5 0xd5 > +#define CH_NUM_8 0xe0 > +#define AUD_LAYOUT 0x01 > + > +#define GPIO_1_CONTROL 0xd6 > +#define GPIO_1_PULL_UP 0x04 > +#define GPIO_1_OEN 0x02 > +#define GPIO_1_DATA 0x01 > + > +#define TX_ANALOG_DEBUG2 0xdd > +#define POWERON_TIME_1P5MS 0x03 > + > +#define TX_PLL_FILTER 0xdf > +#define PD_RING_OSC 0x40 > +#define V33_SWITCH_ON 0x08 > + > +#define TX_PLL_FILTER5 0xe0 > +#define SP_TX_ANALOG_CTRL0 0xe1 > +#define P5V_PROTECT 0x80 > +#define SHORT_PROTECT 0x40 > +#define P5V_PROTECT_PD 0x20 > +#define SHORT_PROTECT_PD 0x10 > + > +#define TX_ANALOG_CTRL 0xe5 > +#define SHORT_DPDM 0x4 > + > +#define SP_COMMON_INT_STATUS1 0xf1 > +#define PLL_LOCK_CHG 0x40 > +#define VIDEO_FORMAT_CHG 0x08 > +#define AUDIO_CLK_CHG 0x04 > +#define VIDEO_CLOCK_CHG 0x02 > + > +#define SP_COMMON_INT_STATUS2 0xf2 > +#define HDCP_AUTH_CHG 0x02 > +#define HDCP_AUTH_DONE 0x01 > + > +#define SP_COMMON_INT_STATUS3 0xf3 > +#define HDCP_LINK_CHECK_FAIL 0x01 > + > +#define SP_COMMON_INT_STATUS4 0xf4 > +#define PLUG 0x01 > +#define ESYNC_ERR 0x10 > +#define HPD_LOST 0x02 > +#define HPD_CHANGE 0x04 > +#define HPD_IRQ 0x40 > + > +#define SP_TX_INT_STATUS1 0xf7 > +#define DPCD_IRQ_REQUEST 0x80 > +#define HPD 0x40 > +#define TRAINING_FINISH 0x20 > +#define POLLING_ERR 0x10 > +#define LINK_CHANGE 0x04 > +#define SINK_CHG 0x08 > + > +#define SP_COMMON_INT_MASK1 0xf8 > +#define SP_COMMON_INT_MASK2 0xf9 > +#define SP_COMMON_INT_MASK3 0xfa > +#define SP_COMMON_INT_MASK4 0xfb > +#define SP_INT_MASK 0xfe > +#define SP_TX_INT_CTRL_REG 0xff > + > +/***************************************************************/ > +/* Register definition of device address 0x7a */ > +/***************************************************************/ > + > +#define SP_TX_LT_CTRL_REG0 0x30 > +#define SP_TX_LT_CTRL_REG1 0x31 > +#define SP_TX_LT_CTRL_REG2 0x34 > +#define SP_TX_LT_CTRL_REG3 0x35 > +#define SP_TX_LT_CTRL_REG4 0x36 > +#define SP_TX_LT_CTRL_REG5 0x37 > +#define SP_TX_LT_CTRL_REG6 0x38 > +#define SP_TX_LT_CTRL_REG7 0x39 > +#define SP_TX_LT_CTRL_REG8 0x3a > +#define SP_TX_LT_CTRL_REG9 0x3b > +#define SP_TX_LT_CTRL_REG10 0x40 > +#define SP_TX_LT_CTRL_REG11 0x41 > +#define SP_TX_LT_CTRL_REG12 0x44 > +#define SP_TX_LT_CTRL_REG13 0x45 > +#define SP_TX_LT_CTRL_REG14 0x46 > +#define SP_TX_LT_CTRL_REG15 0x47 > +#define SP_TX_LT_CTRL_REG16 0x48 > +#define SP_TX_LT_CTRL_REG17 0x49 > +#define SP_TX_LT_CTRL_REG18 0x4a > +#define SP_TX_LT_CTRL_REG19 0x4b > +#define SP_TX_LT_TEST_PATTERN_REG0 0x80 > +#define SP_TX_LT_TEST_PATTERN_REG1 0x81 > +#define SP_TX_LT_TEST_PATTERN_REG2 0x82 > +#define SP_TX_LT_TEST_PATTERN_REG3 0x83 > +#define SP_TX_LT_TEST_PATTERN_REG4 0x84 > +#define SP_TX_LT_TEST_PATTERN_REG5 0x85 > +#define SP_TX_LT_TEST_PATTERN_REG6 0x86 > +#define SP_TX_LT_TEST_PATTERN_REG7 0x87 > +#define SP_TX_LT_TEST_PATTERN_REG8 0x88 > +#define SP_TX_LT_TEST_PATTERN_REG9 0x89 > + > +#define SP_TX_AUD_INTERFACE_CTRL0 0x5f > +#define AUD_INTERFACE_DISABLE 0x80 > + > +#define SP_TX_AUD_INTERFACE_CTRL2 0x60 > +#define M_AUD_ADJUST_ST 0x04 > + > +#define SP_TX_AUD_INTERFACE_CTRL3 0x62 > +#define SP_TX_AUD_INTERFACE_CTRL4 0x67 > +#define SP_TX_AUD_INTERFACE_CTRL5 0x68 > +#define SP_TX_AUD_INTERFACE_CTRL6 0x69 > + > +#define OCM_REG3 0x96 > +#define OCM_RST 0x80 > + > +#define FW_VER_REG 0xb7 > + > +/***************************************************************/ > +/* Definition of DPCD */ > +/***************************************************************/ > + > +#define DOWN_R_TERM_DET _BIT6 > +#define SRAM_EEPROM_LOAD_DONE _BIT5 > +#define SRAM_CRC_CHK_DONE _BIT4 > +#define SRAM_CRC_CHK_PASS _BIT3 > +#define DOWN_STRM_ENC _BIT2 > +#define DOWN_STRM_AUTH _BIT1 > +#define DOWN_STRM_HPD _BIT0 > + > +#define DPCD_DPCD_REV 0x00 > +#define DPCD_MAX_LINK_RATE 0x01 > + > +#define DPCD_MAX_LANE_COUNT 0x02 > +#define ENHANCED_FRAME_CAP 0x80 > + > +#define DPCD_MAX_DOWNSPREAD 0x03 > +#define DPCD_NORP 0x04 > +#define DPCD_DSPORT_PRESENT 0x05 > + > +#define DPCD_LINK_BW_SET 0x00 > +#define DPCD_LANE_COUNT_SET 0x01 > +#define ENHANCED_FRAME_EN 0x80 > + > +#define DPCD_TRAINING_PATTERN_SET 0x02 > +#define DPCD_TRAINNIG_LANE0_SET 0x03 > + > +#define DPCD_DOWNSPREAD_CTRL 0x07 > +#define SPREAD_AMPLITUDE 0x10 > + > +#define DPCD_SINK_COUNT 0x00 > +#define DPCD_SERVICE_IRQ_VECTOR 0x01 > +#define TEST_IRQ 0x02 > +#define CP_IRQ 0x04 > +#define SINK_SPECIFIC_IRQ 0x40 > + > +#define DPCD_LANE0_1_STATUS 0x02 > + > +#define DPCD_LANE_ALIGN_UD 0x04 > +#define DPCD_SINK_STATUS 0x05 > + > +#define DPCD_TEST_RESPONSE 0x60 > +#define TEST_ACK 0x01 > +#define DPCD_TEST_EDID_CHECKSUM_WRITE 0x04 > + > +#define DPCD_TEST_EDID_CHECKSUM 0x61 > + > +#define DPCD_SPECIFIC_INTERRUPT1 0x10 > +#define DPCD_USER_COMM1 0x22 > + > +#define DPCD_SPECIFIC_INTERRUPT2 0x11 > + > +#define DPCD_TEST_REQUEST 0x18 > +#define DPCD_TEST_LINK_RATE 0x19 > + > +#define DPCD_TEST_LANE_COUNT 0x20 > + > +#define DPCD_PHY_TEST_PATTERN 0x48 > + > +#endif > + > -- > 2.1.0 >
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig index 2de52a5..aa6fe12 100644 --- a/drivers/gpu/drm/bridge/Kconfig +++ b/drivers/gpu/drm/bridge/Kconfig @@ -29,4 +29,6 @@ config DRM_PARADE_PS8622 ---help--- Parade eDP-LVDS bridge chip driver. +source "drivers/gpu/drm/bridge/anx78xx/Kconfig" + endmenu diff --git a/drivers/gpu/drm/bridge/Makefile b/drivers/gpu/drm/bridge/Makefile index e2eef1c..e5bd38b 100644 --- a/drivers/gpu/drm/bridge/Makefile +++ b/drivers/gpu/drm/bridge/Makefile @@ -3,3 +3,4 @@ ccflags-y := -Iinclude/drm obj-$(CONFIG_DRM_DW_HDMI) += dw_hdmi.o obj-$(CONFIG_DRM_NXP_PTN3460) += nxp-ptn3460.o obj-$(CONFIG_DRM_PARADE_PS8622) += parade-ps8622.o +obj-$(CONFIG_DRM_ANX78XX) += anx78xx/ diff --git a/drivers/gpu/drm/bridge/anx78xx/Kconfig b/drivers/gpu/drm/bridge/anx78xx/Kconfig new file mode 100644 index 0000000..08f9c08 --- /dev/null +++ b/drivers/gpu/drm/bridge/anx78xx/Kconfig @@ -0,0 +1,7 @@ +config DRM_ANX78XX + tristate "Analogix ANX78XX bridge" + help + ANX78XX is a HD video transmitter chip over micro-USB + connector for smartphone device. + + diff --git a/drivers/gpu/drm/bridge/anx78xx/Makefile b/drivers/gpu/drm/bridge/anx78xx/Makefile new file mode 100644 index 0000000..a843733 --- /dev/null +++ b/drivers/gpu/drm/bridge/anx78xx/Makefile @@ -0,0 +1,4 @@ +obj-${CONFIG_DRM_ANX78XX} := anx78xx.o + +anx78xx-y += anx78xx_main.o +anx78xx-y += slimport_tx_drv.o diff --git a/drivers/gpu/drm/bridge/anx78xx/anx78xx.h b/drivers/gpu/drm/bridge/anx78xx/anx78xx.h new file mode 100644 index 0000000..f62c8e7 --- /dev/null +++ b/drivers/gpu/drm/bridge/anx78xx/anx78xx.h @@ -0,0 +1,41 @@ +/* + * Copyright(c) 2015, Analogix Semiconductor. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __ANX78xx_H +#define __ANX78xx_H + +#include <linux/i2c.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <linux/gpio/consumer.h> + +struct anx78xx_platform_data { + struct gpio_desc *gpiod_pd; + struct gpio_desc *gpiod_reset; + spinlock_t lock; +}; + +struct anx78xx { + struct i2c_client *client; + struct anx78xx_platform_data *pdata; + struct delayed_work work; + struct workqueue_struct *workqueue; + struct mutex lock; +}; + +void anx78xx_poweron(struct anx78xx *data); +void anx78xx_poweroff(struct anx78xx *data); + +#endif diff --git a/drivers/gpu/drm/bridge/anx78xx/anx78xx_main.c b/drivers/gpu/drm/bridge/anx78xx/anx78xx_main.c new file mode 100644 index 0000000..1e4a87e --- /dev/null +++ b/drivers/gpu/drm/bridge/anx78xx/anx78xx_main.c @@ -0,0 +1,228 @@ +/* + * Copyright(c) 2015, Analogix Semiconductor. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/err.h> +#include <linux/async.h> +#include <linux/of_gpio.h> +#include <linux/of_platform.h> +#include <linux/delay.h> + +#include "anx78xx.h" +#include "slimport_tx_drv.h" + +void anx78xx_poweron(struct anx78xx *anx78xx) +{ + struct anx78xx_platform_data *pdata = anx78xx->pdata; + + gpiod_set_value_cansleep(pdata->gpiod_reset, 0); + usleep_range(1000, 2000); + + gpiod_set_value_cansleep(pdata->gpiod_pd, 0); + usleep_range(1000, 2000); + + gpiod_set_value_cansleep(pdata->gpiod_reset, 1); +} + +void anx78xx_poweroff(struct anx78xx *anx78xx) +{ + struct anx78xx_platform_data *pdata = anx78xx->pdata; + + gpiod_set_value_cansleep(pdata->gpiod_reset, 0); + usleep_range(1000, 2000); + + gpiod_set_value_cansleep(pdata->gpiod_pd, 1); + usleep_range(1000, 2000); +} + +static int anx78xx_init_gpio(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + struct anx78xx_platform_data *pdata = anx78xx->pdata; + + /* gpio for chip power down */ + pdata->gpiod_pd = devm_gpiod_get(dev, "pd", GPIOD_OUT_HIGH); + if (IS_ERR(pdata->gpiod_pd)) { + dev_err(dev, "unable to claim pd gpio\n"); + return PTR_ERR(pdata->gpiod_pd); + } + + /* gpio for chip reset */ + pdata->gpiod_reset = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(pdata->gpiod_reset)) { + dev_err(dev, "unable to claim reset gpio\n"); + return PTR_ERR(pdata->gpiod_reset); + } + + return 0; +} + +static int anx78xx_system_init(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + + if (!sp_chip_detect(anx78xx)) { + anx78xx_poweroff(anx78xx); + dev_err(dev, "failed to detect anx78xx\n"); + return -ENODEV; + } + + sp_tx_variable_init(); + return 0; +} + +static void anx78xx_work_func(struct work_struct *work) +{ + struct anx78xx *anx78xx = container_of(work, struct anx78xx, + work.work); + int workqueue_timer = 0; + + if (sp_tx_current_state() >= STATE_PLAY_BACK) + workqueue_timer = 500; + else + workqueue_timer = 100; + mutex_lock(&anx78xx->lock); + sp_main_process(anx78xx); + mutex_unlock(&anx78xx->lock); + queue_delayed_work(anx78xx->workqueue, &anx78xx->work, + msecs_to_jiffies(workqueue_timer)); +} + +static int anx78xx_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct anx78xx *anx78xx; + int ret; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_I2C_BLOCK)) { + dev_err(&client->dev, "i2c bus does not support the device\n"); + return -ENODEV; + } + + anx78xx = devm_kzalloc(&client->dev, sizeof(*anx78xx), GFP_KERNEL); + if (!anx78xx) + return -ENOMEM; + + anx78xx->pdata = devm_kzalloc(&client->dev, + sizeof(struct anx78xx_platform_data), + GFP_KERNEL); + if (!anx78xx->pdata) + return -ENOMEM; + + anx78xx->client = client; + + i2c_set_clientdata(client, anx78xx); + + mutex_init(&anx78xx->lock); + + ret = anx78xx_init_gpio(anx78xx); + if (ret) { + dev_err(&client->dev, "failed to initialize gpios\n"); + return ret; + } + + INIT_DELAYED_WORK(&anx78xx->work, anx78xx_work_func); + + anx78xx->workqueue = create_singlethread_workqueue("anx78xx_work"); + if (!anx78xx->workqueue) { + dev_err(&client->dev, "failed to create work queue\n"); + return -ENOMEM; + } + + ret = anx78xx_system_init(anx78xx); + if (ret) { + dev_err(&client->dev, "failed to initialize anx78xx\n"); + goto cleanup; + } + + /* enable driver */ + queue_delayed_work(anx78xx->workqueue, &anx78xx->work, 0); + + return 0; + +cleanup: + destroy_workqueue(anx78xx->workqueue); + return ret; +} + +static int anx78xx_i2c_remove(struct i2c_client *client) +{ + struct anx78xx *anx78xx = i2c_get_clientdata(client); + + destroy_workqueue(anx78xx->workqueue); + + return 0; +} + +static int anx78xx_i2c_suspend(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct anx78xx *anx78xx = i2c_get_clientdata(client); + + cancel_delayed_work_sync(&anx78xx->work); + flush_workqueue(anx78xx->workqueue); + anx78xx_poweroff(anx78xx); + sp_tx_clean_state_machine(); + + return 0; +} + +static int anx78xx_i2c_resume(struct device *dev) +{ + struct i2c_client *client = container_of(dev, struct i2c_client, dev); + struct anx78xx *anx78xx = i2c_get_clientdata(client); + + queue_delayed_work(anx78xx->workqueue, &anx78xx->work, 0); + + return 0; +} + +static SIMPLE_DEV_PM_OPS(anx78xx_i2c_pm_ops, + anx78xx_i2c_suspend, anx78xx_i2c_resume); + +static const struct i2c_device_id anx78xx_id[] = { + {"anx7814", 0}, + { /* sentinel */ } +}; + +MODULE_DEVICE_TABLE(i2c, anx78xx_id); + +static const struct of_device_id anx78xx_match_table[] = { + {.compatible = "analogix,anx7814",}, + { /* sentinel */ }, +}; + +MODULE_DEVICE_TABLE(of, anx78xx_match_table); + +static struct i2c_driver anx78xx_driver = { + .driver = { + .name = "anx7814", + .pm = &anx78xx_i2c_pm_ops, + .of_match_table = of_match_ptr(anx78xx_match_table), + }, + .probe = anx78xx_i2c_probe, + .remove = anx78xx_i2c_remove, + .id_table = anx78xx_id, +}; + +module_i2c_driver(anx78xx_driver); + +MODULE_DESCRIPTION("Slimport transmitter ANX78XX driver"); +MODULE_AUTHOR("Junhua Xia <jxia@analogixsemi.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION("1.1"); diff --git a/drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c b/drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c new file mode 100644 index 0000000..7721326 --- /dev/null +++ b/drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c @@ -0,0 +1,3148 @@ +/* + * Copyright(c) 2015, Analogix Semiconductor. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include <linux/delay.h> +#include <linux/types.h> + +#include "anx78xx.h" +#include "slimport_tx_drv.h" + +struct slimport { + int block_en; /* HDCP control enable/ disable from AP */ + + u8 tx_test_bw; + bool tx_test_lt; + bool tx_test_edid; + + u8 changed_bandwidth; + + u8 hdmi_dvi_status; + u8 need_clean_status; + + u8 ds_vid_stb_cntr; + u8 hdcp_fail_count; + + u8 edid_break; + u8 edid_checksum; + u8 edid_blocks[256]; + + u8 read_edid_flag; + + u8 down_sample_en; + + struct packet_avi tx_packet_avi; + struct packet_spd tx_packet_spd; + struct packet_mpeg tx_packet_mpeg; + struct audio_info_frame tx_audioinfoframe; + + struct common_int common_int_status; + struct hdmi_rx_int hdmi_rx_int_status; + + enum sp_tx_state tx_system_state; + enum sp_tx_state tx_system_state_bak; + enum audio_output_status tx_ao_state; + enum video_output_status tx_vo_state; + enum sink_connection_status tx_sc_state; + enum sp_tx_lt_status tx_lt_state; + enum hdcp_status hcdp_state; +}; + +static struct slimport sp; + +static const u16 chipid_list[] = { + 0x7818, + 0x7816, + 0x7814, + 0x7812, + 0x7810, + 0x7806, + 0x7802 +}; + +static void sp_hdmi_rx_new_vsi_int(struct anx78xx *anx78xx); +static u8 sp_hdcp_cap_check(struct anx78xx *anx78xx); +static void sp_tx_show_information(struct anx78xx *anx78xx); +static void sp_print_system_state(struct anx78xx *anx78xx, u8 ss); + +static int sp_read_reg(struct anx78xx *anx78xx, u8 slave_addr, + u8 offset, u8 *buf) +{ + int ret; + struct i2c_client *client = anx78xx->client; + + client->addr = slave_addr >> 1; + + ret = i2c_smbus_read_byte_data(client, offset); + if (ret < 0) { + dev_err(&client->dev, "failed to read i2c addr=%x\n", + slave_addr); + return ret; + } + + *buf = ret; + + return 0; +} + +static int sp_write_reg(struct anx78xx *anx78xx, u8 slave_addr, + u8 offset, u8 value) +{ + int ret; + struct i2c_client *client = anx78xx->client; + + client->addr = slave_addr >> 1; + + ret = i2c_smbus_write_byte_data(client, offset, value); + if (ret < 0) + dev_err(&client->dev, "failed to write i2c addr=%x\n", + slave_addr); + + return ret; +} + +static u8 sp_i2c_read_byte(struct anx78xx *anx78xx, + u8 dev, u8 offset) +{ + u8 ret; + + sp_read_reg(anx78xx, dev, offset, &ret); + return ret; +} + +static void sp_reg_bit_ctl(struct anx78xx *anx78xx, u8 addr, u8 offset, + u8 data, bool enable) +{ + u8 regval; + + sp_read_reg(anx78xx, addr, offset, ®val); + if (enable) { + if ((regval & data) != data) { + regval |= data; + sp_write_reg(anx78xx, addr, offset, regval); + } + } else { + if ((regval & data) == data) { + regval &= ~data; + sp_write_reg(anx78xx, addr, offset, regval); + } + } +} + +static inline void sp_write_reg_or(struct anx78xx *anx78xx, u8 address, + u8 offset, u8 mask) +{ + sp_write_reg(anx78xx, address, offset, + sp_i2c_read_byte(anx78xx, address, offset) | mask); +} + +static inline void sp_write_reg_and(struct anx78xx *anx78xx, u8 address, + u8 offset, u8 mask) +{ + sp_write_reg(anx78xx, address, offset, + sp_i2c_read_byte(anx78xx, address, offset) & mask); +} + +static inline void sp_write_reg_and_or(struct anx78xx *anx78xx, u8 address, + u8 offset, u8 and_mask, u8 or_mask) +{ + sp_write_reg(anx78xx, address, offset, + (sp_i2c_read_byte(anx78xx, address, offset) & and_mask) + | or_mask); +} + +static inline void sp_write_reg_or_and(struct anx78xx *anx78xx, u8 address, + u8 offset, u8 or_mask, u8 and_mask) +{ + sp_write_reg(anx78xx, address, offset, + (sp_i2c_read_byte(anx78xx, address, offset) | or_mask) + & and_mask); +} + +static inline void sp_tx_video_mute(struct anx78xx *anx78xx, bool enable) +{ + sp_reg_bit_ctl(anx78xx, TX_P2, VID_CTRL1, VIDEO_MUTE, enable); +} + +static inline void hdmi_rx_mute_audio(struct anx78xx *anx78xx, bool enable) +{ + sp_reg_bit_ctl(anx78xx, RX_P0, RX_MUTE_CTRL, AUD_MUTE, enable); +} + +static inline void hdmi_rx_mute_video(struct anx78xx *anx78xx, bool enable) +{ + sp_reg_bit_ctl(anx78xx, RX_P0, RX_MUTE_CTRL, VID_MUTE, enable); +} + +static inline void sp_tx_addronly_set(struct anx78xx *anx78xx, bool enable) +{ + sp_reg_bit_ctl(anx78xx, TX_P0, AUX_CTRL2, ADDR_ONLY_BIT, enable); +} + +static inline void sp_tx_set_link_bw(struct anx78xx *anx78xx, u8 bw) +{ + sp_write_reg(anx78xx, TX_P0, SP_TX_LINK_BW_SET_REG, bw); +} + +static inline u8 sp_tx_get_link_bw(struct anx78xx *anx78xx) +{ + return (sp_i2c_read_byte(anx78xx, TX_P0, SP_TX_LINK_BW_SET_REG) & + LINK_BW_SET_MASK); +} + +static inline bool sp_tx_get_pll_lock_status(struct anx78xx *anx78xx) +{ + u8 byte; + + byte = sp_i2c_read_byte(anx78xx, TX_P0, TX_DEBUG1); + + return (byte & DEBUG_PLL_LOCK) != 0; +} + +static inline void gen_m_clk_with_downspeading(struct anx78xx *anx78xx) +{ + sp_write_reg_or(anx78xx, TX_P0, SP_TX_M_CALCU_CTRL, M_GEN_CLK_SEL); +} + +static inline void gen_m_clk_without_downspeading(struct anx78xx *anx78xx) +{ + sp_write_reg_and(anx78xx, TX_P0, SP_TX_M_CALCU_CTRL, (~M_GEN_CLK_SEL)); +} + +static inline void hdmi_rx_set_hpd(struct anx78xx *anx78xx, bool enable) +{ + if (enable) + sp_write_reg_or(anx78xx, TX_P2, SP_TX_VID_CTRL3_REG, HPD_OUT); + else + sp_write_reg_and(anx78xx, TX_P2, SP_TX_VID_CTRL3_REG, + ~HPD_OUT); +} + +static inline void hdmi_rx_set_termination(struct anx78xx *anx78xx, + bool enable) +{ + if (enable) + sp_write_reg_and(anx78xx, RX_P0, HDMI_RX_TMDS_CTRL_REG7, + ~TERM_PD); + else + sp_write_reg_or(anx78xx, RX_P0, HDMI_RX_TMDS_CTRL_REG7, + TERM_PD); +} + +static inline void sp_tx_clean_hdcp_status(struct anx78xx *anx78xx) +{ + sp_write_reg(anx78xx, TX_P0, TX_HDCP_CTRL0, 0x03); + sp_write_reg_or(anx78xx, TX_P0, TX_HDCP_CTRL0, RE_AUTH); + usleep_range(2000, 4000); +} + +static inline void sp_tx_link_phy_initialization(struct anx78xx *anx78xx) +{ + sp_write_reg(anx78xx, TX_P2, SP_TX_ANALOG_CTRL0, 0x02); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG0, 0x01); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG10, 0x00); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG1, 0x03); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG11, 0x00); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG2, 0x07); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG12, 0x00); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG3, 0x7f); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG13, 0x00); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG4, 0x71); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG14, 0x0c); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG5, 0x6b); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG15, 0x42); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG6, 0x7f); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG16, 0x1e); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG7, 0x73); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG17, 0x3e); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG8, 0x7f); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG18, 0x72); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG9, 0x7f); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_CTRL_REG19, 0x7e); +} + +static inline void sp_tx_set_sys_state(struct anx78xx *anx78xx, u8 ss) +{ + struct device *dev = &anx78xx->client->dev; + + dev_dbg(dev, "set: clean_status: %x,\n", sp.need_clean_status); + + if ((sp.tx_system_state >= STATE_LINK_TRAINING) && + (ss < STATE_LINK_TRAINING)) + sp_write_reg_or(anx78xx, TX_P0, SP_TX_ANALOG_PD_REG, CH0_PD); + + sp.tx_system_state_bak = sp.tx_system_state; + sp.tx_system_state = ss; + sp.need_clean_status = 1; + sp_print_system_state(anx78xx, sp.tx_system_state); +} + +static inline void reg_hardware_reset(struct anx78xx *anx78xx) +{ + sp_write_reg_or(anx78xx, TX_P2, SP_TX_RST_CTRL_REG, HW_RST); + sp_tx_clean_state_machine(); + sp_tx_set_sys_state(anx78xx, STATE_SP_INITIALIZED); + msleep(500); +} + +static inline void write_dpcd_addr(struct anx78xx *anx78xx, u8 addrh, + u8 addrm, u8 addrl) +{ + u8 regval; + + if (sp_i2c_read_byte(anx78xx, TX_P0, AUX_ADDR_7_0) != addrl) + sp_write_reg(anx78xx, TX_P0, AUX_ADDR_7_0, addrl); + + if (sp_i2c_read_byte(anx78xx, TX_P0, AUX_ADDR_15_8) != addrm) + sp_write_reg(anx78xx, TX_P0, AUX_ADDR_15_8, addrm); + + sp_read_reg(anx78xx, TX_P0, AUX_ADDR_19_16, ®val); + + if ((regval & 0x0f) != (addrh & 0x0f)) + sp_write_reg(anx78xx, TX_P0, AUX_ADDR_19_16, + (regval & 0xf0) | addrh); +} + +static inline void goto_next_system_state(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + + dev_dbg(dev, "next: clean_status: %x,\n", sp.need_clean_status); + + sp.tx_system_state_bak = sp.tx_system_state; + sp.tx_system_state++; + sp_print_system_state(anx78xx, sp.tx_system_state); +} + +static inline void redo_cur_system_state(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + + dev_dbg(dev, "redo: clean_status: %x,\n", sp.need_clean_status); + + sp.need_clean_status = 1; + sp.tx_system_state_bak = sp.tx_system_state; + sp_print_system_state(anx78xx, sp.tx_system_state); +} + +static inline void system_state_change_with_case(struct anx78xx *anx78xx, + u8 status) +{ + struct device *dev = &anx78xx->client->dev; + + if (sp.tx_system_state < status) + return; + + dev_dbg(dev, "change_case: clean_status: %xm,\n", + sp.need_clean_status); + + if (sp.tx_system_state >= STATE_LINK_TRAINING && + status < STATE_LINK_TRAINING) + sp_write_reg_or(anx78xx, TX_P0, SP_TX_ANALOG_PD_REG, + CH0_PD); + + sp.need_clean_status = 1; + sp.tx_system_state_bak = sp.tx_system_state; + sp.tx_system_state = status; + sp_print_system_state(anx78xx, sp.tx_system_state); +} + +static void sp_wait_aux_op_finish(struct anx78xx *anx78xx, u8 *err_flag) +{ + u8 cnt; + u8 regval; + struct device *dev = &anx78xx->client->dev; + + *err_flag = 0; + cnt = 150; + while (sp_i2c_read_byte(anx78xx, TX_P0, AUX_CTRL2) & AUX_OP_EN) { + usleep_range(2000, 4000); + if (cnt-- == 0) { + dev_err(dev, "aux operate failed!\n"); + *err_flag = 1; + break; + } + } + + sp_read_reg(anx78xx, TX_P0, SP_TX_AUX_STATUS, ®val); + if (regval & 0x0f) { + dev_err(dev, "wait aux operation status %.2x\n", regval); + *err_flag = 1; + } +} + +static void sp_print_system_state(struct anx78xx *anx78xx, u8 ss) +{ + struct device *dev = &anx78xx->client->dev; + + switch (ss) { + case STATE_WAITTING_CABLE_PLUG: + dev_dbg(dev, "-STATE_WAITTING_CABLE_PLUG-\n"); + break; + case STATE_SP_INITIALIZED: + dev_dbg(dev, "-STATE_SP_INITIALIZED-\n"); + break; + case STATE_SINK_CONNECTION: + dev_dbg(dev, "-STATE_SINK_CONNECTION-\n"); + break; + case STATE_PARSE_EDID: + dev_dbg(dev, "-STATE_PARSE_EDID-\n"); + break; + case STATE_LINK_TRAINING: + dev_dbg(dev, "-STATE_LINK_TRAINING-\n"); + break; + case STATE_VIDEO_OUTPUT: + dev_dbg(dev, "-STATE_VIDEO_OUTPUT-\n"); + break; + case STATE_HDCP_AUTH: + dev_dbg(dev, "-STATE_HDCP_AUTH-\n"); + break; + case STATE_AUDIO_OUTPUT: + dev_dbg(dev, "-STATE_AUDIO_OUTPUT-\n"); + break; + case STATE_PLAY_BACK: + dev_dbg(dev, "-STATE_PLAY_BACK-\n"); + break; + default: + dev_err(dev, "unknown system state\n"); + break; + } +} + +static void sp_tx_rst_aux(struct anx78xx *anx78xx) +{ + sp_write_reg_or(anx78xx, TX_P2, RST_CTRL2, AUX_RST); + sp_write_reg_and(anx78xx, TX_P2, RST_CTRL2, ~AUX_RST); +} + +static u8 sp_tx_aux_dpcdread_bytes(struct anx78xx *anx78xx, u8 addrh, + u8 addrm, u8 addrl, u8 ccount, u8 *pbuf) +{ + u8 regval, regval1, i; + u8 bok; + struct device *dev = &anx78xx->client->dev; + + sp_write_reg(anx78xx, TX_P0, BUF_DATA_COUNT, 0x80); + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, ((ccount - 1) << 4) | 0x09); + write_dpcd_addr(anx78xx, addrh, addrm, addrl); + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, AUX_OP_EN); + usleep_range(2000, 4000); + + sp_wait_aux_op_finish(anx78xx, &bok); + if (bok) { + dev_err(dev, "aux read failed\n"); + sp_read_reg(anx78xx, TX_P2, SP_TX_INT_STATUS1, ®val); + sp_read_reg(anx78xx, TX_P0, TX_DEBUG1, ®val1); + if (!(regval1 & POLLING_EN) || (regval & POLLING_ERR)) + sp_tx_rst_aux(anx78xx); + return 1; + } + + for (i = 0; i < ccount; i++) { + sp_read_reg(anx78xx, TX_P0, BUF_DATA_0 + i, ®val); + *(pbuf + i) = regval; + if (i >= MAX_BUF_CNT) + break; + } + return 0; +} + +static u8 sp_tx_aux_dpcdwrite_bytes(struct anx78xx *anx78xx, u8 addrh, + u8 addrm, u8 addrl, u8 ccount, u8 *pbuf) +{ + u8 regval, i, ret; + + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, ((ccount - 1) << 4) | 0x08); + write_dpcd_addr(anx78xx, addrh, addrm, addrl); + for (i = 0; i < ccount; i++) { + regval = *pbuf; + pbuf++; + sp_write_reg(anx78xx, TX_P0, BUF_DATA_0 + i, regval); + + if (i >= 15) + break; + } + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, AUX_OP_EN); + sp_wait_aux_op_finish(anx78xx, &ret); + return ret; +} + +static u8 sp_tx_aux_dpcdwrite_byte(struct anx78xx *anx78xx, u8 addrh, + u8 addrm, u8 addrl, u8 data1) +{ + u8 ret; + + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, 0x08); + write_dpcd_addr(anx78xx, addrh, addrm, addrl); + sp_write_reg(anx78xx, TX_P0, BUF_DATA_0, data1); + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, AUX_OP_EN); + sp_wait_aux_op_finish(anx78xx, &ret); + return ret; +} + +static void sp_block_power_ctrl(struct anx78xx *anx78xx, + enum sp_tx_power_block sp_tx_pd_block, + u8 power) +{ + struct device *dev = &anx78xx->client->dev; + + if (power == SP_POWER_ON) + sp_write_reg_and(anx78xx, TX_P2, SP_POWERD_CTRL_REG, + ~sp_tx_pd_block); + else + sp_write_reg_or(anx78xx, TX_P2, SP_POWERD_CTRL_REG, + sp_tx_pd_block); + + dev_dbg(dev, "sp_tx_power_on: %.2x\n", sp_tx_pd_block); +} + +static void sp_vbus_power_off(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + int i; + + for (i = 0; i < 5; i++) { + sp_write_reg_and(anx78xx, TX_P2, TX_PLL_FILTER5, + ~P5V_PROTECT_PD & ~SHORT_PROTECT_PD); + sp_write_reg_or(anx78xx, TX_P2, TX_PLL_FILTER, V33_SWITCH_ON); + if (!(sp_i2c_read_byte(anx78xx, TX_P2, TX_PLL_FILTER5) + & 0xc0)) { + dev_dbg(dev, "3.3V output enabled\n"); + break; + } + } +} + +void sp_tx_clean_state_machine(void) +{ + sp.tx_system_state = STATE_WAITTING_CABLE_PLUG; + sp.tx_system_state_bak = STATE_WAITTING_CABLE_PLUG; + sp.tx_sc_state = SC_INIT; + sp.tx_lt_state = LT_INIT; + sp.hcdp_state = HDCP_CAPABLE_CHECK; + sp.tx_vo_state = VO_WAIT_VIDEO_STABLE; + sp.tx_ao_state = AO_INIT; +} + +enum sp_tx_state sp_tx_current_state(void) +{ + return sp.tx_system_state; +} + +void sp_tx_variable_init(void) +{ + sp.block_en = 1; + + sp.tx_system_state = STATE_WAITTING_CABLE_PLUG; + sp.tx_system_state_bak = STATE_WAITTING_CABLE_PLUG; + + sp.edid_break = 0; + sp.read_edid_flag = 0; + sp.edid_checksum = 0; + + memset(sp.edid_blocks, 0, 256); + + sp.tx_lt_state = LT_INIT; + sp.hcdp_state = HDCP_CAPABLE_CHECK; + sp.need_clean_status = 0; + sp.tx_sc_state = SC_INIT; + sp.tx_vo_state = VO_WAIT_VIDEO_STABLE; + sp.tx_ao_state = AO_INIT; + sp.changed_bandwidth = LINK_5P4G; + sp.hdmi_dvi_status = HDMI_MODE; + + sp.tx_test_lt = 0; + sp.tx_test_bw = 0; + sp.tx_test_edid = 0; + + sp.ds_vid_stb_cntr = 0; + sp.hdcp_fail_count = 0; +} + +static void hdmi_rx_tmds_phy_initialization(struct anx78xx *anx78xx) +{ + sp_write_reg(anx78xx, RX_P0, HDMI_RX_TMDS_CTRL_REG2, 0xa9); + sp_write_reg(anx78xx, RX_P0, HDMI_RX_TMDS_CTRL_REG7, 0x80); + + sp_write_reg(anx78xx, RX_P0, HDMI_RX_TMDS_CTRL_REG1, 0x90); + sp_write_reg(anx78xx, RX_P0, HDMI_RX_TMDS_CTRL_REG6, 0x92); + sp_write_reg(anx78xx, RX_P0, HDMI_RX_TMDS_CTRL_REG20, 0xf2); +} + +static void hdmi_rx_initialization(struct anx78xx *anx78xx) +{ + sp_write_reg(anx78xx, RX_P0, RX_MUTE_CTRL, AUD_MUTE | VID_MUTE); + sp_write_reg_or(anx78xx, RX_P0, RX_CHIP_CTRL, + MAN_HDMI5V_DET | PLLLOCK_CKDT_EN | DIGITAL_CKDT_EN); + + sp_write_reg_or(anx78xx, RX_P0, RX_SRST, HDCP_MAN_RST | SW_MAN_RST | + TMDS_RST | VIDEO_RST); + sp_write_reg_and(anx78xx, RX_P0, RX_SRST, ~HDCP_MAN_RST & + ~SW_MAN_RST & ~TMDS_RST & ~VIDEO_RST); + + sp_write_reg_or(anx78xx, RX_P0, RX_AEC_EN0, AEC_EN06 | AEC_EN05); + sp_write_reg_or(anx78xx, RX_P0, RX_AEC_EN2, AEC_EN21); + sp_write_reg_or(anx78xx, RX_P0, RX_AEC_CTRL, AVC_EN | AAC_OE | AAC_EN); + + sp_write_reg_and(anx78xx, RX_P0, RX_SYS_PWDN1, ~PWDN_CTRL); + + sp_write_reg_or(anx78xx, RX_P0, RX_VID_DATA_RNG, R2Y_INPUT_LIMIT); + sp_write_reg(anx78xx, RX_P0, 0x65, 0xc4); + sp_write_reg(anx78xx, RX_P0, 0x66, 0x18); + + /* enable DDC stretch */ + sp_write_reg(anx78xx, TX_P0, TX_EXTRA_ADDR, 0x50); + + hdmi_rx_tmds_phy_initialization(anx78xx); + hdmi_rx_set_hpd(anx78xx, 0); + hdmi_rx_set_termination(anx78xx, 0); +} + +struct anx78xx_clock_data const pxtal_data[XTAL_CLK_NUM] = { + {19, 192}, + {24, 240}, + {25, 250}, + {26, 260}, + {27, 270}, + {38, 384}, + {52, 520}, + {27, 270}, +}; + +static void xtal_clk_sel(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + + dev_dbg(dev, "define XTAL_CLK: %x\n", XTAL_27M); + sp_write_reg_and_or(anx78xx, TX_P2, + TX_ANALOG_DEBUG2, ~0x3c, 0x3c & (XTAL_27M << 2)); + sp_write_reg(anx78xx, TX_P0, 0xec, pxtal_data[XTAL_27M].xtal_clk_m10); + sp_write_reg(anx78xx, TX_P0, 0xed, + ((pxtal_data[XTAL_27M].xtal_clk_m10 & 0xff00) >> 2) + | pxtal_data[XTAL_27M].xtal_clk); + + sp_write_reg(anx78xx, TX_P0, I2C_GEN_10US_TIMER0, + pxtal_data[XTAL_27M].xtal_clk_m10); + sp_write_reg(anx78xx, TX_P0, I2C_GEN_10US_TIMER1, + (pxtal_data[XTAL_27M].xtal_clk_m10 & 0xff00) >> 8); + sp_write_reg(anx78xx, TX_P0, 0xbf, pxtal_data[XTAL_27M].xtal_clk - 1); + + sp_write_reg_and_or(anx78xx, RX_P0, 0x49, 0x07, + ((pxtal_data[XTAL_27M].xtal_clk >> 1) - 2) << 3); +} + +void sp_tx_initialization(struct anx78xx *anx78xx) +{ + sp_write_reg(anx78xx, TX_P0, AUX_CTRL2, 0x30); + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, 0x08); + + sp_write_reg_and(anx78xx, TX_P0, TX_HDCP_CTRL, + (u8)~AUTO_EN & ~AUTO_START); + sp_write_reg(anx78xx, TX_P0, OTP_KEY_PROTECT1, OTP_PSW1); + sp_write_reg(anx78xx, TX_P0, OTP_KEY_PROTECT2, OTP_PSW2); + sp_write_reg(anx78xx, TX_P0, OTP_KEY_PROTECT3, OTP_PSW3); + sp_write_reg_or(anx78xx, TX_P0, HDCP_KEY_CMD, DISABLE_SYNC_HDCP); + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL8_REG, VID_VRES_TH); + + sp_write_reg(anx78xx, TX_P0, HDCP_AUTO_TIMER, HDCP_AUTO_TIMER_VAL); + sp_write_reg_or(anx78xx, TX_P0, TX_HDCP_CTRL, LINK_POLLING); + + sp_write_reg_or(anx78xx, TX_P0, TX_LINK_DEBUG, M_VID_DEBUG); + sp_write_reg_or(anx78xx, TX_P2, TX_ANALOG_DEBUG2, POWERON_TIME_1P5MS); + + xtal_clk_sel(anx78xx); + sp_write_reg(anx78xx, TX_P0, AUX_DEFER_CTRL, 0x8c); + + sp_write_reg_or(anx78xx, TX_P0, TX_DP_POLLING, AUTO_POLLING_DISABLE); + /* + * Short the link intergrity check timer to speed up bstatus + * polling for HDCP CTS item 1A-07 + */ + sp_write_reg(anx78xx, TX_P0, SP_TX_LINK_CHK_TIMER, 0x1d); + sp_write_reg_or(anx78xx, TX_P0, TX_MISC, EQ_TRAINING_LOOP); + + sp_write_reg_or(anx78xx, TX_P0, SP_TX_ANALOG_PD_REG, CH0_PD); + + sp_write_reg(anx78xx, TX_P2, SP_TX_INT_CTRL_REG, 0x01); + /* disable HDCP mismatch function for VGA dongle */ + sp_tx_link_phy_initialization(anx78xx); + gen_m_clk_with_downspeading(anx78xx); + + sp.down_sample_en = 0; +} + +bool sp_chip_detect(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + u16 id; + u8 idh = 0, idl = 0; + int i; + + anx78xx_poweron(anx78xx); + + /* check chip id */ + sp_read_reg(anx78xx, TX_P2, SP_TX_DEV_IDL_REG, &idl); + sp_read_reg(anx78xx, TX_P2, SP_TX_DEV_IDH_REG, &idh); + id = idl | (idh << 8); + + dev_dbg(dev, "CHIPID: ANX%x\n", id & 0xffff); + + for (i = 0; i < ARRAY_SIZE(chipid_list); i++) { + if (id == chipid_list[i]) + return true; + } + + return false; +} + +static void sp_waiting_cable_plug_process(struct anx78xx *anx78xx) +{ + sp_tx_variable_init(); + anx78xx_poweron(anx78xx); + goto_next_system_state(anx78xx); +} + +/* + * Check if it is ANALOGIX dongle. + */ +static const u8 ANX_OUI[3] = {0x00, 0x22, 0xb9}; + +static u8 is_anx_dongle(struct anx78xx *anx78xx) +{ + u8 buf[3]; + + /* 0x0500~0x0502: BRANCH_IEEE_OUI */ + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x05, 0x00, 3, buf); + + if (!memcmp(buf, ANX_OUI, 3)) + return 1; + + return 0; +} + +static void sp_tx_get_rx_bw(struct anx78xx *anx78xx, u8 *bw) +{ + if (is_anx_dongle(anx78xx)) + *bw = LINK_6P75G; /* just for debug */ + else + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x00, + DPCD_MAX_LINK_RATE, 1, bw); +} + +static u8 sp_tx_get_cable_type(struct anx78xx *anx78xx, + enum cable_type_status det_cable_type_state) +{ + struct device *dev = &anx78xx->client->dev; + + u8 ds_port_preset; + u8 aux_status; + u8 data_buf[16]; + u8 cur_cable_type; + + ds_port_preset = 0; + cur_cable_type = DWN_STRM_IS_NULL; + + aux_status = sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x00, 0x05, 1, + &ds_port_preset); + + dev_dbg(dev, "DPCD 0x005: %x\n", (int)ds_port_preset); + + switch (det_cable_type_state) { + case CHECK_AUXCH: + if (aux_status == 0) { + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x00, 0, 0x0c, + data_buf); + det_cable_type_state = GETTED_CABLE_TYPE; + } else { + dev_err(dev, "AUX access error\n"); + break; + } + case GETTED_CABLE_TYPE: + switch ((ds_port_preset & (BIT(1) | BIT(2))) >> 1) { + case 0x00: + cur_cable_type = DWN_STRM_IS_DIGITAL; + dev_dbg(dev, "Downstream is DP dongle.\n"); + break; + case 0x01: + case 0x03: + cur_cable_type = DWN_STRM_IS_ANALOG; + dev_dbg(dev, "Downstream is VGA dongle.\n"); + break; + case 0x02: + cur_cable_type = DWN_STRM_IS_HDMI; + dev_dbg(dev, "Downstream is HDMI dongle.\n"); + break; + default: + cur_cable_type = DWN_STRM_IS_NULL; + dev_err(dev, "Downstream can not recognized.\n"); + break; + } + default: + break; + } + return cur_cable_type; +} + +static u8 sp_tx_get_dp_connection(struct anx78xx *anx78xx) +{ + u8 regval; + + if (sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, + DPCD_SINK_COUNT, 1, ®val)) + return 0; + + if (regval & 0x1f) { + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x00, 0x04, 1, ®val); + if (regval & 0x20) { + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x06, 0x00, 1, + ®val); + /* + * Bit 5 = SET_DN_DEVICE_DP_PWR_5V + * Bit 6 = SET_DN_DEVICE_DP_PWR_12V + * Bit 7 = SET_DN_DEVICE_DP_PWR_18V + */ + regval = regval & 0x1f; + sp_tx_aux_dpcdwrite_byte(anx78xx, 0x00, 0x06, 0x00, + regval | 0x20); + } + return 1; + } else { + return 0; + } +} + +static void sp_sink_connection(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + + switch (sp.tx_sc_state) { + case SC_INIT: + sp.tx_sc_state++; + case SC_CHECK_CABLE_TYPE: + case SC_WAITTING_CABLE_TYPE: + default: + if (sp_tx_get_cable_type(anx78xx, CHECK_AUXCH) == + DWN_STRM_IS_NULL) { + sp.tx_sc_state++; + if (sp.tx_sc_state >= SC_WAITTING_CABLE_TYPE) { + sp.tx_sc_state = SC_NOT_CABLE; + dev_dbg(dev, "Can not get cable type!\n"); + } + break; + } + + sp.tx_sc_state = SC_SINK_CONNECTED; + case SC_SINK_CONNECTED: + if (sp_tx_get_dp_connection(anx78xx)) + goto_next_system_state(anx78xx); + break; + case SC_NOT_CABLE: + sp_vbus_power_off(anx78xx); + reg_hardware_reset(anx78xx); + break; + } +} + +/******************start EDID process********************/ +static void sp_tx_enable_video_input(struct anx78xx *anx78xx, u8 enable) +{ + struct device *dev = &anx78xx->client->dev; + u8 regval; + + sp_read_reg(anx78xx, TX_P2, VID_CTRL1, ®val); + if (enable) { + sp_write_reg(anx78xx, TX_P2, VID_CTRL1, + (regval & 0xf7) | VIDEO_EN); + dev_dbg(dev, "Slimport Video is enabled!\n"); + + } else { + sp_write_reg(anx78xx, TX_P2, VID_CTRL1, regval & ~VIDEO_EN); + dev_dbg(dev, "Slimport Video is disabled!\n"); + } +} + +static u8 sp_get_edid_detail(u8 *data_buf) +{ + u16 pixclock_edid; + + pixclock_edid = (((u16)data_buf[1] << 8) | ((u16)data_buf[0] & 0xff)); + if (pixclock_edid <= 5300) + return LINK_1P62G; + else if ((pixclock_edid > 5300) && (pixclock_edid <= 8900)) + return LINK_2P7G; + else if ((pixclock_edid > 8900) && (pixclock_edid <= 18000)) + return LINK_5P4G; + else + return LINK_6P75G; +} + +static u8 sp_parse_edid_to_get_bandwidth(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + u8 i, bandwidth, temp; + + bandwidth = LINK_1P62G; + for (i = 0; i < 4; i++) { + if (sp.edid_blocks[0x36 + 0x12 * i] == 0) + break; + temp = sp_get_edid_detail(sp.edid_blocks + 0x36 + 0x12 * i); + dev_dbg(dev, "bandwidth via EDID : %x\n", temp); + if (bandwidth < temp) + bandwidth = temp; + if (bandwidth >= LINK_6P75G) + break; + } + + return bandwidth; +} + +static void sp_tx_aux_wr(struct anx78xx *anx78xx, u8 offset) +{ + sp_write_reg(anx78xx, TX_P0, BUF_DATA_0, offset); + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, 0x04); + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, AUX_OP_EN); + sp_wait_aux_op_finish(anx78xx, &sp.edid_break); +} + +static void sp_tx_aux_rd(struct anx78xx *anx78xx, u8 len_cmd) +{ + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, len_cmd); + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, AUX_OP_EN); + sp_wait_aux_op_finish(anx78xx, &sp.edid_break); +} + +static u8 sp_tx_get_edid_block(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + u8 regval; + + sp_tx_aux_wr(anx78xx, 0x7e); + sp_tx_aux_rd(anx78xx, 0x01); + sp_read_reg(anx78xx, TX_P0, BUF_DATA_0, ®val); + dev_dbg(dev, "EDID Block = %d\n", regval + 1); + + if (regval > 3) + regval = 1; + return regval; +} + +static void sp_edid_read(struct anx78xx *anx78xx, u8 offset, + u8 *pblock_buf) +{ + u8 data_cnt, error_cnt; + u8 regval; + + sp_tx_aux_wr(anx78xx, offset); + sp_tx_aux_rd(anx78xx, 0xf5); + data_cnt = 0; + error_cnt = 0; + + while ((data_cnt) < 16) { + sp_read_reg(anx78xx, TX_P0, BUF_DATA_COUNT, ®val); + + if (regval & 0x1f) { + data_cnt = data_cnt + (regval & 0x1f); + do { + sp_read_reg(anx78xx, TX_P0, + BUF_DATA_0 + regval - 1, + &pblock_buf[regval - 1]); + } while (--regval); + } else { + if (error_cnt++ <= 2) { + sp_tx_rst_aux(anx78xx); + regval = 0x05 | ((0x0f - data_cnt) << 4); + sp_tx_aux_rd(anx78xx, regval); + } else { + sp.edid_break = 1; + break; + } + } + } + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, 0x01); + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, ADDR_ONLY_BIT | AUX_OP_EN); + sp_wait_aux_op_finish(anx78xx, &sp.edid_break); + sp_tx_addronly_set(anx78xx, 0); +} + +static void sp_tx_edid_read_initial(struct anx78xx *anx78xx) +{ + sp_write_reg(anx78xx, TX_P0, AUX_ADDR_7_0, 0x50); + sp_write_reg(anx78xx, TX_P0, AUX_ADDR_15_8, 0); + sp_write_reg_and(anx78xx, TX_P0, AUX_ADDR_19_16, 0xf0); +} + +static void sp_seg_edid_read(struct anx78xx *anx78xx, + u8 segment, u8 offset) +{ + struct device *dev = &anx78xx->client->dev; + u8 regval, cnt; + int i; + + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, 0x04); + + sp_write_reg(anx78xx, TX_P0, AUX_ADDR_7_0, 0x30); + + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, ADDR_ONLY_BIT | AUX_OP_EN); + + sp_read_reg(anx78xx, TX_P0, AUX_CTRL2, ®val); + + sp_wait_aux_op_finish(anx78xx, &sp.edid_break); + sp_read_reg(anx78xx, TX_P0, AUX_CTRL, ®val); + + sp_write_reg(anx78xx, TX_P0, BUF_DATA_0, segment); + + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, 0x04); + + sp_write_reg_and_or(anx78xx, TX_P0, AUX_CTRL2, ~ADDR_ONLY_BIT, + AUX_OP_EN); + cnt = 0; + sp_read_reg(anx78xx, TX_P0, AUX_CTRL2, ®val); + while (regval & AUX_OP_EN) { + usleep_range(1000, 2000); + cnt++; + if (cnt == 10) { + dev_err(dev, "read AUX_CTRL2 failed.\n"); + sp_tx_rst_aux(anx78xx); + cnt = 0; + sp.edid_break = 1; + return; + } + sp_read_reg(anx78xx, TX_P0, AUX_CTRL2, ®val); + } + + sp_write_reg(anx78xx, TX_P0, AUX_ADDR_7_0, 0x50); + + sp_tx_aux_wr(anx78xx, offset); + + sp_tx_aux_rd(anx78xx, 0xf5); + cnt = 0; + for (i = 0; i < 16; i++) { + sp_read_reg(anx78xx, TX_P0, BUF_DATA_COUNT, ®val); + while ((regval & 0x1f) == 0) { + usleep_range(2000, 4000); + cnt++; + sp_read_reg(anx78xx, TX_P0, BUF_DATA_COUNT, ®val); + if (cnt == 10) { + dev_err(dev, + "read BUF_DATA_COUNT failed.\n"); + dev_dbg(dev, "read break"); + sp_tx_rst_aux(anx78xx); + sp.edid_break = 1; + return; + } + } + + sp_read_reg(anx78xx, TX_P0, BUF_DATA_0 + i, ®val); + } + + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, 0x01); + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, ADDR_ONLY_BIT | AUX_OP_EN); + sp_write_reg_and(anx78xx, TX_P0, AUX_CTRL2, ~ADDR_ONLY_BIT); + sp_read_reg(anx78xx, TX_P0, AUX_CTRL2, ®val); + + cnt = 0; + while (regval & AUX_OP_EN) { + usleep_range(1000, 2000); + cnt++; + if (cnt == 10) { + dev_err(dev, "read AUX_CTRL2 failed.\n"); + sp_tx_rst_aux(anx78xx); + cnt = 0; + sp.edid_break = 1; + return; + } + sp_read_reg(anx78xx, TX_P0, AUX_CTRL2, ®val); + } +} + +static bool sp_edid_checksum_result(u8 *pbuf) +{ + u8 cnt, checksum; + + checksum = 0; + + for (cnt = 0; cnt < 0x80; cnt++) + checksum = checksum + pbuf[cnt]; + + sp.edid_checksum = checksum - pbuf[0x7f]; + sp.edid_checksum = ~sp.edid_checksum + 1; + + return checksum == 0 ? 1 : 0; +} + +static void sp_check_edid_data(struct anx78xx *anx78xx, u8 *pbuf) +{ + struct device *dev = &anx78xx->client->dev; + u8 i; + + if ((pbuf[0] == 0x00) && (pbuf[1] == 0xff) && + (pbuf[2] == 0xff) && (pbuf[3] == 0xff) && + (pbuf[4] == 0xff) && (pbuf[5] == 0xff) && + (pbuf[6] == 0xff) && (pbuf[7] == 0x00)) + dev_dbg(dev, "Good EDID header!\n"); + else + dev_err(dev, "Bad EDID header!\n"); + + for (i = 0; i <= (pbuf[0x7e] > 1 ? 1 : pbuf[0x7e]); i++) { + if (!sp_edid_checksum_result(pbuf + i * 128)) + dev_err(dev, "Block %x edid checksum error\n", i); + else + dev_dbg(dev, "Block %x edid checksum OK\n", i); + } +} + +static void sp_tx_edid_read(struct anx78xx *anx78xx, u8 *pedid_blocks_buf) +{ + struct device *dev = &anx78xx->client->dev; + u8 offset = 0; + u8 count, blocks_num; + u8 pblock_buf[16]; + u8 i, j, regval; + + sp.edid_break = 0; + sp_tx_edid_read_initial(anx78xx); + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, 0x04); + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, 0x03); + sp_wait_aux_op_finish(anx78xx, &sp.edid_break); + sp_tx_addronly_set(anx78xx, 0); + + blocks_num = sp_tx_get_edid_block(anx78xx); + + count = 0; + do { + switch (count) { + case 0: + case 1: + for (i = 0; i < 8; i++) { + offset = (i + count * 8) * 16; + sp_edid_read(anx78xx, offset, pblock_buf); + if (sp.edid_break == 1) + break; + for (j = 0; j < 16; j++) { + pedid_blocks_buf[offset + j] + = pblock_buf[j]; + } + } + break; + case 2: + case 3: + if (count == 2) + offset = 0x00; + else /* count == 3 */ + offset = 0x80; + for (j = 0; j < 8; j++) { + if (sp.edid_break == 1) + break; + sp_seg_edid_read(anx78xx, count / 2, offset); + offset = offset + 0x10; + } + break; + default: + break; + } + count++; + if (sp.edid_break == 1) + break; + } while (blocks_num >= count); + + sp_tx_rst_aux(anx78xx); + if (sp.read_edid_flag == 0) { + sp_check_edid_data(anx78xx, pedid_blocks_buf); + sp.read_edid_flag = 1; + } + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x18, 1, ®val); + if (regval & 0x04) { + dev_dbg(dev, "check sum = %.2x\n", sp.edid_checksum); + regval = sp.edid_checksum; + sp_tx_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x02, 0x61, 1, + ®val); + sp.tx_test_edid = 1; + regval = 0x04; + sp_tx_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x02, 0x60, 1, + ®val); + dev_dbg(dev, "Test EDID done\n"); + } +} + +static bool sp_check_with_pre_edid(struct anx78xx *anx78xx, u8 *org_buf) +{ + struct device *dev = &anx78xx->client->dev; + u8 i; + u8 buf[16]; + bool ret = false; + + sp.edid_break = 0; + sp_tx_edid_read_initial(anx78xx); + sp_write_reg(anx78xx, TX_P0, AUX_CTRL, 0x04); + sp_write_reg_or(anx78xx, TX_P0, AUX_CTRL2, 0x03); + sp_wait_aux_op_finish(anx78xx, &sp.edid_break); + sp_tx_addronly_set(anx78xx, 0); + + sp_edid_read(anx78xx, 0x70, buf); + + if (sp.edid_break == 0) { + for (i = 0; i < 16; i++) { + if (org_buf[0x70 + i] != buf[i]) { + dev_dbg(dev, "%s\n", + "different checksum and blocks num\n"); + goto return_point; + } + } + } else { + goto return_point; + } + + sp_edid_read(anx78xx, 0x08, buf); + if (sp.edid_break == 0) { + for (i = 0; i < 16; i++) { + if (org_buf[i + 8] != buf[i]) { + dev_dbg(dev, "different edid information\n"); + goto return_point; + } + } + } else { + goto return_point; + } + + ret = true; +return_point: + sp_tx_rst_aux(anx78xx); + + return ret; +} + +static void sp_edid_process(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + u8 temp_value, temp_value1; + u8 i; + + dev_dbg(dev, "edid_process\n"); + + if (sp.read_edid_flag == 1) + if (!sp_check_with_pre_edid(anx78xx, sp.edid_blocks)) + sp.read_edid_flag = 0; + + if (sp.read_edid_flag == 0) { + sp_tx_edid_read(anx78xx, sp.edid_blocks); + if (sp.edid_break) + dev_err(dev, "ERR:EDID corruption!\n"); + } + + /* Release the HPD after the OTP loaddown */ + for (i = 0; i < 10; i++) { + if (sp_i2c_read_byte(anx78xx, TX_P0, HDCP_KEY_STATUS) & 0x01) + break; + + dev_dbg(dev, "waiting HDCP KEY loaddown\n"); + usleep_range(1000, 2000); + } + + sp_write_reg(anx78xx, RX_P0, HDMI_RX_INT_MASK1_REG, 0xe2); + hdmi_rx_set_hpd(anx78xx, 1); + dev_dbg(dev, "hdmi_rx_set_hpd 1 !\n"); + + hdmi_rx_set_termination(anx78xx, 1); + + sp_tx_get_rx_bw(anx78xx, &temp_value); + dev_dbg(dev, "RX BW %x\n", temp_value); + + temp_value1 = sp_parse_edid_to_get_bandwidth(anx78xx); + if (temp_value <= temp_value1) + temp_value1 = temp_value; + + dev_dbg(dev, "set link bw in edid %x\n", temp_value1); + sp.changed_bandwidth = temp_value1; + goto_next_system_state(anx78xx); +} + +/******************End EDID process********************/ + +/******************start Link training process********************/ +static void sp_tx_lvttl_bit_mapping(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + u8 regval, colorspace; + u8 vid_bit; + + vid_bit = 0; + sp_read_reg(anx78xx, RX_P1, HDMI_RX_AVI_DATA00_REG, &colorspace); + colorspace &= 0x60; + + switch (((sp_i2c_read_byte(anx78xx, RX_P0, HDMI_RX_VIDEO_STATUS_REG1) + & COLOR_DEPTH) >> 4)) { + default: + case HDMI_LEGACY: + regval = IN_BPC_8BIT; + vid_bit = 0; + break; + case HDMI_24BIT: + regval = IN_BPC_8BIT; + if (colorspace == 0x20) + vid_bit = 5; + else + vid_bit = 1; + break; + case HDMI_30BIT: + regval = IN_BPC_10BIT; + if (colorspace == 0x20) + vid_bit = 6; + else + vid_bit = 2; + break; + case HDMI_36BIT: + regval = IN_BPC_12BIT; + if (colorspace == 0x20) + vid_bit = 6; + else + vid_bit = 3; + break; + } + + /* + * For down sample video (12bit, 10bit ---> 8bit), + * this register doesn't change + */ + if (sp.down_sample_en == 0) + sp_write_reg_and_or(anx78xx, TX_P2, + SP_TX_VID_CTRL2_REG, 0x8c, + colorspace >> 5 | regval); + + /* Patch: for 10bit video must be set this value to 12bit by someone */ + if (sp.down_sample_en == 1 && regval == IN_BPC_10BIT) + vid_bit = 3; + + sp_write_reg_and_or(anx78xx, TX_P2, + BIT_CTRL_SPECIFIC, 0x00, + ENABLE_BIT_CTRL | vid_bit << 1); + + if (sp.tx_test_edid) { + sp_write_reg_and(anx78xx, TX_P2, SP_TX_VID_CTRL2_REG, 0x8f); + dev_dbg(dev, "***color space is set to 18bit***\n"); + } + + if (colorspace) { + sp_write_reg(anx78xx, TX_P0, SP_TX_VID_BLANK_SET1, 0x80); + sp_write_reg(anx78xx, TX_P0, SP_TX_VID_BLANK_SET2, 0x00); + sp_write_reg(anx78xx, TX_P0, SP_TX_VID_BLANK_SET3, 0x80); + } else { + sp_write_reg(anx78xx, TX_P0, SP_TX_VID_BLANK_SET1, 0x0); + sp_write_reg(anx78xx, TX_P0, SP_TX_VID_BLANK_SET2, 0x0); + sp_write_reg(anx78xx, TX_P0, SP_TX_VID_BLANK_SET3, 0x0); + } +} + +static unsigned long sp_tx_pclk_calc(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + unsigned long str_plck; + u16 vid_counter; + u8 regval; + + sp_read_reg(anx78xx, RX_P0, PCLK_HR_CNT2, ®val); + vid_counter = regval << 8; + sp_read_reg(anx78xx, RX_P0, PCLK_HR_CNT1, ®val); + vid_counter |= regval; + str_plck = (vid_counter * pxtal_data[XTAL_27M].xtal_clk_m10) >> 12; + dev_dbg(dev, "PCLK = %d.%d\n", (u16)str_plck / 10, + (u16)(str_plck - ((str_plck / 10) * 10))); + return str_plck; +} + +static u8 sp_tx_bw_lc_sel(struct anx78xx *anx78xx, unsigned long pclk) +{ + struct device *dev = &anx78xx->client->dev; + unsigned long pixel_clk; + u8 link; + + switch (((sp_i2c_read_byte(anx78xx, RX_P0, HDMI_RX_VIDEO_STATUS_REG1) + & COLOR_DEPTH) >> 4)) { + case HDMI_LEGACY: + case HDMI_24BIT: + default: + pixel_clk = pclk; + break; + case HDMI_30BIT: + pixel_clk = (pclk * 5) >> 2; + break; + case HDMI_36BIT: + pixel_clk = (pclk * 3) >> 1; + break; + } + + dev_dbg(dev, "pixel_clk = %d.%d\n", (u16)pixel_clk / 10, + (u16)(pixel_clk - ((pixel_clk / 10) * 10))); + + sp.down_sample_en = 0; + if (pixel_clk <= 530) { + link = LINK_1P62G; + } else if ((530 < pixel_clk) && (pixel_clk <= 890)) { + link = LINK_2P7G; + } else if ((890 < pixel_clk) && (pixel_clk <= 1800)) { + link = LINK_5P4G; + } else { + link = LINK_6P75G; + if (pixel_clk > 2240) + sp.down_sample_en = 1; + } + + if (sp_tx_get_link_bw(anx78xx) != link) { + sp.changed_bandwidth = link; + dev_dbg(dev, + "different bandwidth between sink and video %.2x", + link); + return 1; + } + return 0; +} + +static void sp_tx_spread_enable(struct anx78xx *anx78xx, u8 benable) +{ + u8 regval; + + sp_read_reg(anx78xx, TX_P0, SP_TX_DOWN_SPREADING_CTRL1, ®val); + + if (benable) { + regval |= SP_TX_SSC_DWSPREAD; + sp_write_reg(anx78xx, TX_P0, SP_TX_DOWN_SPREADING_CTRL1, + regval); + + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x01, + DPCD_DOWNSPREAD_CTRL, 1, ®val); + regval |= SPREAD_AMPLITUDE; + sp_tx_aux_dpcdwrite_byte(anx78xx, 0x00, 0x01, + DPCD_DOWNSPREAD_CTRL, regval); + } else { + regval &= ~SP_TX_SSC_DISABLE; + sp_write_reg(anx78xx, TX_P0, SP_TX_DOWN_SPREADING_CTRL1, + regval); + + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x01, + DPCD_DOWNSPREAD_CTRL, 1, ®val); + regval &= ~SPREAD_AMPLITUDE; + sp_tx_aux_dpcdwrite_byte(anx78xx, 0x00, 0x01, + DPCD_DOWNSPREAD_CTRL, regval); + } +} + +static void sp_tx_config_ssc(struct anx78xx *anx78xx, + enum sp_ssc_dep sscdep) +{ + sp_write_reg(anx78xx, TX_P0, SP_TX_DOWN_SPREADING_CTRL1, 0x0); + sp_write_reg(anx78xx, TX_P0, SP_TX_DOWN_SPREADING_CTRL1, sscdep); + sp_tx_spread_enable(anx78xx, 1); +} + +static void sp_tx_enhancemode_set(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + u8 regval; + + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x00, DPCD_MAX_LANE_COUNT, + 1, ®val); + if (regval & ENHANCED_FRAME_CAP) { + sp_write_reg_or(anx78xx, TX_P0, SP_TX_SYS_CTRL4_REG, + ENHANCED_MODE); + + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x01, + DPCD_LANE_COUNT_SET, 1, ®val); + regval |= ENHANCED_FRAME_EN; + sp_tx_aux_dpcdwrite_byte(anx78xx, 0x00, 0x01, + DPCD_LANE_COUNT_SET, regval); + + dev_dbg(dev, "Enhance mode enabled\n"); + } else { + sp_write_reg_and(anx78xx, TX_P0, SP_TX_SYS_CTRL4_REG, + ~ENHANCED_MODE); + + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x01, + DPCD_LANE_COUNT_SET, 1, ®val); + + regval &= ~ENHANCED_FRAME_EN; + sp_tx_aux_dpcdwrite_byte(anx78xx, 0x00, 0x01, + DPCD_LANE_COUNT_SET, regval); + + dev_dbg(dev, "Enhance mode disabled\n"); + } +} + +static u16 sp_tx_link_err_check(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + u16 errl = 0, errh = 0; + u8 bytebuf[2]; + + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x10, 2, bytebuf); + usleep_range(5000, 10000); + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x10, 2, bytebuf); + errh = bytebuf[1]; + + if (errh & 0x80) { + errl = bytebuf[0]; + errh = (errh & 0x7f) << 8; + errl = errh + errl; + } + + dev_err(dev, " Err of Lane = %d\n", errl); + return errl; +} + +static void sp_lt_finish(struct anx78xx *anx78xx, u8 temp_value) +{ + struct device *dev = &anx78xx->client->dev; + + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x02, 1, &temp_value); + if ((temp_value & 0x07) == 0x07) { + /* + * if there is link error, + * adjust pre-emphsis to check error again. + * If there is no error,keep the setting, + * otherwise use 400mv0db + */ + if (!sp.tx_test_lt) { + if (sp_tx_link_err_check(anx78xx)) { + sp_read_reg(anx78xx, TX_P0, + SP_TX_LT_SET_REG, &temp_value); + if (!(temp_value & MAX_PRE_REACH)) { + sp_write_reg(anx78xx, TX_P0, + SP_TX_LT_SET_REG, + temp_value + 0x08); + if (sp_tx_link_err_check(anx78xx)) + sp_write_reg(anx78xx, TX_P0, + SP_TX_LT_SET_REG, + temp_value); + } + } + + temp_value = sp_tx_get_link_bw(anx78xx); + if (temp_value == sp.changed_bandwidth) { + dev_dbg(dev, "LT succeed, bw: %.2x", + temp_value); + dev_dbg(dev, "Lane0 Set: %.2x\n", + sp_i2c_read_byte(anx78xx, TX_P0, + SP_TX_LT_SET_REG)); + sp.tx_lt_state = LT_INIT; + goto_next_system_state(anx78xx); + } else { + dev_dbg(dev, "cur:%.2x, per:%.2x\n", + temp_value, + sp.changed_bandwidth); + sp.tx_lt_state = LT_ERROR; + } + } else { + sp.tx_test_lt = 0; + sp.tx_lt_state = LT_INIT; + goto_next_system_state(anx78xx); + } + } else { + dev_dbg(dev, "LANE0 Status error: %.2x\n", + temp_value & 0x07); + sp.tx_lt_state = LT_ERROR; + } +} + +static void sp_link_training(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + u8 value, regval; + + dev_dbg(dev, "sp.tx_lt_state : %x\n", (int)sp.tx_lt_state); + + switch (sp.tx_lt_state) { + case LT_INIT: + sp_block_power_ctrl(anx78xx, SP_TX_PWR_VIDEO, SP_POWER_ON); + sp_tx_video_mute(anx78xx, 1); + sp_tx_enable_video_input(anx78xx, 0); + sp.tx_lt_state++; + /* fallthrough */ + case LT_WAIT_PLL_LOCK: + if (!sp_tx_get_pll_lock_status(anx78xx)) { + sp_read_reg(anx78xx, TX_P0, SP_TX_PLL_CTRL_REG, + &value); + + value |= PLL_RST; + sp_write_reg(anx78xx, TX_P0, SP_TX_PLL_CTRL_REG, + value); + + value &= ~PLL_RST; + sp_write_reg(anx78xx, TX_P0, SP_TX_PLL_CTRL_REG, + value); + + dev_dbg(dev, "PLL not lock!\n"); + } else { + sp.tx_lt_state = LT_CHECK_LINK_BW; + } + SP_BREAK(LT_WAIT_PLL_LOCK, sp.tx_lt_state); + /* fallthrough */ + case LT_CHECK_LINK_BW: + sp_tx_get_rx_bw(anx78xx, &value); + if (value < sp.changed_bandwidth) { + dev_dbg(dev, "****Over bandwidth****\n"); + sp.changed_bandwidth = value; + } else { + sp.tx_lt_state++; + } + /* fallthrough */ + case LT_START: + if (sp.tx_test_lt) { + sp.changed_bandwidth = sp.tx_test_bw; + sp_write_reg_and(anx78xx, TX_P2, SP_TX_VID_CTRL2_REG, + 0x8f); + } else { + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, 0x00); + } + + sp_write_reg_and(anx78xx, TX_P0, SP_TX_ANALOG_PD_REG, + ~CH0_PD); + + sp_tx_config_ssc(anx78xx, SSC_DEP_4000PPM); + sp_tx_set_link_bw(anx78xx, sp.changed_bandwidth); + sp_tx_enhancemode_set(anx78xx); + + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x00, 0x00, 0x01, + ®val); + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x06, 0x00, 0x01, + &value); + if (regval >= 0x12) + value &= 0xf8; + else + value &= 0xfc; + value |= 0x01; + sp_tx_aux_dpcdwrite_byte(anx78xx, 0x00, 0x06, 0x00, value); + + sp_write_reg(anx78xx, TX_P0, LT_CTRL, SP_TX_LT_EN); + sp.tx_lt_state = LT_WAITTING_FINISH; + /* fallthrough */ + case LT_WAITTING_FINISH: + /* here : waiting interrupt to change training state. */ + break; + case LT_ERROR: + sp_write_reg_or(anx78xx, TX_P2, RST_CTRL2, SERDES_FIFO_RST); + msleep(20); + sp_write_reg_and(anx78xx, TX_P2, RST_CTRL2, ~SERDES_FIFO_RST); + dev_err(dev, "LT ERROR Status: SERDES FIFO reset."); + redo_cur_system_state(anx78xx); + sp.tx_lt_state = LT_INIT; + break; + case LT_FINISH: + sp_lt_finish(anx78xx, value); + break; + default: + break; + } +} + +/******************End Link training process********************/ + +/******************Start Output video process********************/ +static void sp_tx_set_colorspace(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + u8 color_space; + + if (sp.down_sample_en) { + sp_read_reg(anx78xx, RX_P1, HDMI_RX_AVI_DATA00_REG, + &color_space); + color_space &= 0x60; + if (color_space == 0x20) { + dev_dbg(dev, "YCbCr4:2:2 ---> PASS THROUGH.\n"); + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL6_REG, + 0x00); + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL5_REG, + 0x00); + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL2_REG, + 0x11); + } else if (color_space == 0x40) { + dev_dbg(dev, "YCbCr4:4:4 ---> YCbCr4:2:2\n"); + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL6_REG, + 0x41); + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL5_REG, + 0x00); + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL2_REG, + 0x12); + } else if (color_space == 0x00) { + dev_dbg(dev, "RGB4:4:4 ---> YCbCr4:2:2\n"); + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL6_REG, + 0x41); + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL5_REG, + 0x83); + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL2_REG, + 0x10); + } + } else { + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL6_REG, 0x00); + sp_write_reg(anx78xx, TX_P2, SP_TX_VID_CTRL5_REG, 0x00); + } +} + +static void sp_tx_avi_setup(struct anx78xx *anx78xx) +{ + u8 regval; + int i; + + for (i = 0; i < 13; i++) { + sp_read_reg(anx78xx, RX_P1, (HDMI_RX_AVI_DATA00_REG + i), + ®val); + sp.tx_packet_avi.avi_data[i] = regval; + } +} + +static void sp_tx_load_packet(struct anx78xx *anx78xx, + enum packets_type type) +{ + int i; + u8 regval; + + switch (type) { + case AVI_PACKETS: + sp_write_reg(anx78xx, TX_P2, SP_TX_AVI_TYPE, 0x82); + sp_write_reg(anx78xx, TX_P2, SP_TX_AVI_VER, 0x02); + sp_write_reg(anx78xx, TX_P2, SP_TX_AVI_LEN, 0x0d); + + for (i = 0; i < 13; i++) { + sp_write_reg(anx78xx, TX_P2, SP_TX_AVI_DB0 + i, + sp.tx_packet_avi.avi_data[i]); + } + + break; + + case SPD_PACKETS: + sp_write_reg(anx78xx, TX_P2, SP_TX_SPD_TYPE, 0x83); + sp_write_reg(anx78xx, TX_P2, SP_TX_SPD_VER, 0x01); + sp_write_reg(anx78xx, TX_P2, SP_TX_SPD_LEN, 0x19); + + for (i = 0; i < 25; i++) { + sp_write_reg(anx78xx, TX_P2, SP_TX_SPD_DB0 + i, + sp.tx_packet_spd.spd_data[i]); + } + + break; + + case VSI_PACKETS: + sp_write_reg(anx78xx, TX_P2, SP_TX_MPEG_TYPE, 0x81); + sp_write_reg(anx78xx, TX_P2, SP_TX_MPEG_VER, 0x01); + sp_read_reg(anx78xx, RX_P1, HDMI_RX_MPEG_LEN_REG, ®val); + sp_write_reg(anx78xx, TX_P2, SP_TX_MPEG_LEN, regval); + + for (i = 0; i < 10; i++) { + sp_write_reg(anx78xx, TX_P2, SP_TX_MPEG_DB0 + i, + sp.tx_packet_mpeg.mpeg_data[i]); + } + + break; + case MPEG_PACKETS: + sp_write_reg(anx78xx, TX_P2, SP_TX_MPEG_TYPE, 0x85); + sp_write_reg(anx78xx, TX_P2, SP_TX_MPEG_VER, 0x01); + sp_write_reg(anx78xx, TX_P2, SP_TX_MPEG_LEN, 0x0d); + + for (i = 0; i < 10; i++) { + sp_write_reg(anx78xx, TX_P2, SP_TX_MPEG_DB0 + i, + sp.tx_packet_mpeg.mpeg_data[i]); + } + + break; + case AUDIF_PACKETS: + sp_write_reg(anx78xx, TX_P2, SP_TX_AUD_TYPE, 0x84); + sp_write_reg(anx78xx, TX_P2, SP_TX_AUD_VER, 0x01); + sp_write_reg(anx78xx, TX_P2, SP_TX_AUD_LEN, 0x0a); + for (i = 0; i < 10; i++) { + sp_write_reg(anx78xx, TX_P2, SP_TX_AUD_DB0 + i, + sp.tx_audioinfoframe.pb_byte[i]); + } + + break; + + default: + break; + } +} + +static void sp_tx_config_packets(struct anx78xx *anx78xx, + enum packets_type type) +{ + u8 regval; + + switch (type) { + case AVI_PACKETS: + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); + regval &= ~AVI_IF_EN; + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); + sp_tx_load_packet(anx78xx, AVI_PACKETS); + + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); + regval |= AVI_IF_UD; + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); + + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); + regval |= AVI_IF_EN; + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); + break; + case SPD_PACKETS: + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); + regval &= ~SPD_IF_EN; + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); + sp_tx_load_packet(anx78xx, SPD_PACKETS); + + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); + regval |= SPD_IF_UD; + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); + + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); + regval |= SPD_IF_EN; + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); + break; + case VSI_PACKETS: + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); + regval &= ~MPEG_IF_EN; + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); + + sp_tx_load_packet(anx78xx, VSI_PACKETS); + + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); + regval |= MPEG_IF_UD; + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); + + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); + regval |= MPEG_IF_EN; + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); + break; + case MPEG_PACKETS: + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); + regval &= ~MPEG_IF_EN; + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); + + sp_tx_load_packet(anx78xx, MPEG_PACKETS); + + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); + regval |= MPEG_IF_UD; + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); + + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); + regval |= MPEG_IF_EN; + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); + break; + case AUDIF_PACKETS: + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); + regval &= ~AUD_IF_EN; + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); + + sp_tx_load_packet(anx78xx, AUDIF_PACKETS); + + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); + regval |= AUD_IF_UP; + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); + + sp_read_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ®val); + regval |= AUD_IF_EN; + sp_write_reg(anx78xx, TX_P0, SP_TX_PKT_EN_REG, regval); + break; + default: + break; + } +} + +static void sp_config_video_output(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + u8 regval; + + switch (sp.tx_vo_state) { + default: + case VO_WAIT_VIDEO_STABLE: + sp_read_reg(anx78xx, RX_P0, HDMI_RX_SYS_STATUS_REG, ®val); + if ((regval & (TMDS_DE_DET | TMDS_CLOCK_DET)) == 0x03) { + sp_tx_bw_lc_sel(anx78xx, sp_tx_pclk_calc(anx78xx)); + sp_tx_enable_video_input(anx78xx, 0); + sp_tx_avi_setup(anx78xx); + sp_tx_config_packets(anx78xx, AVI_PACKETS); + sp_tx_set_colorspace(anx78xx); + sp_tx_lvttl_bit_mapping(anx78xx); + if (sp_i2c_read_byte(anx78xx, RX_P0, RX_PACKET_REV_STA) + & VSI_RCVD) + sp_hdmi_rx_new_vsi_int(anx78xx); + sp_tx_enable_video_input(anx78xx, 1); + sp.tx_vo_state = VO_WAIT_TX_VIDEO_STABLE; + } else { + dev_dbg(dev, "HDMI input video not stable!\n"); + } + SP_BREAK(VO_WAIT_VIDEO_STABLE, sp.tx_vo_state); + /* fallthrough */ + case VO_WAIT_TX_VIDEO_STABLE: + sp_read_reg(anx78xx, TX_P0, SP_TX_SYS_CTRL2_REG, ®val); + sp_write_reg(anx78xx, TX_P0, SP_TX_SYS_CTRL2_REG, regval); + sp_read_reg(anx78xx, TX_P0, SP_TX_SYS_CTRL2_REG, ®val); + if (regval & CHA_STA) { + dev_dbg(dev, "Stream clock not stable!\n"); + } else { + sp_read_reg(anx78xx, TX_P0, SP_TX_SYS_CTRL3_REG, + ®val); + sp_write_reg(anx78xx, TX_P0, SP_TX_SYS_CTRL3_REG, + regval); + sp_read_reg(anx78xx, TX_P0, SP_TX_SYS_CTRL3_REG, + ®val); + if (!(regval & STRM_VALID)) + dev_err(dev, "video stream not valid!\n"); + else + sp.tx_vo_state = VO_CHECK_VIDEO_INFO; + } + SP_BREAK(VO_WAIT_TX_VIDEO_STABLE, sp.tx_vo_state); + /* fallthrough */ + case VO_CHECK_VIDEO_INFO: + if (!sp_tx_bw_lc_sel(anx78xx, sp_tx_pclk_calc(anx78xx))) + sp.tx_vo_state++; + else + sp_tx_set_sys_state(anx78xx, STATE_LINK_TRAINING); + SP_BREAK(VO_CHECK_VIDEO_INFO, sp.tx_vo_state); + /* fallthrough */ + case VO_FINISH: + sp_block_power_ctrl(anx78xx, SP_TX_PWR_AUDIO, SP_POWER_DOWN); + hdmi_rx_mute_video(anx78xx, 0); + sp_tx_video_mute(anx78xx, 0); + sp_tx_show_information(anx78xx); + goto_next_system_state(anx78xx); + break; + } +} + +/******************End Output video process********************/ + +/******************Start HDCP process********************/ +static inline void sp_tx_hdcp_encryption_disable(struct anx78xx *anx78xx) +{ + sp_write_reg_and(anx78xx, TX_P0, TX_HDCP_CTRL0, ~ENC_EN); +} + +static inline void sp_tx_hdcp_encryption_enable(struct anx78xx *anx78xx) +{ + sp_write_reg_or(anx78xx, TX_P0, TX_HDCP_CTRL0, ENC_EN); +} + +static void sp_tx_hw_hdcp_enable(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + u8 regval; + + sp_write_reg_and(anx78xx, TX_P0, TX_HDCP_CTRL0, + ~ENC_EN & ~HARD_AUTH_EN); + sp_write_reg_or(anx78xx, TX_P0, TX_HDCP_CTRL0, + HARD_AUTH_EN | BKSV_SRM_PASS | KSVLIST_VLD | ENC_EN); + + sp_read_reg(anx78xx, TX_P0, TX_HDCP_CTRL0, ®val); + dev_dbg(dev, "TX_HDCP_CTRL0 = %.2x\n", regval); + sp_write_reg(anx78xx, TX_P0, SP_TX_WAIT_R0_TIME, 0xb0); + sp_write_reg(anx78xx, TX_P0, SP_TX_WAIT_KSVR_TIME, 0xc8); + + dev_dbg(dev, "Hardware HDCP is enabled.\n"); +} + +static void sp_hdcp_process(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + + switch (sp.hcdp_state) { + case HDCP_CAPABLE_CHECK: + sp.ds_vid_stb_cntr = 0; + sp.hdcp_fail_count = 0; + if (is_anx_dongle(anx78xx)) + sp.hcdp_state = HDCP_WAITTING_VID_STB; + else + sp.hcdp_state = HDCP_HW_ENABLE; + if (sp.block_en == 0) { + if (sp_hdcp_cap_check(anx78xx) == 0) + sp.hcdp_state = HDCP_NOT_SUPPORT; + } + /* + * Just for debug, pin: P2-2 + * There is a switch to disable/enable HDCP. + */ + sp.hcdp_state = HDCP_NOT_SUPPORT; + /*****************************************/ + SP_BREAK(HDCP_CAPABLE_CHECK, sp.hcdp_state); + /* fallthrough */ + case HDCP_WAITTING_VID_STB: + msleep(100); + sp.hcdp_state = HDCP_HW_ENABLE; + SP_BREAK(HDCP_WAITTING_VID_STB, sp.hcdp_state); + /* fallthrough */ + case HDCP_HW_ENABLE: + sp_tx_video_mute(anx78xx, 1); + sp_tx_clean_hdcp_status(anx78xx); + sp_block_power_ctrl(anx78xx, SP_TX_PWR_HDCP, SP_POWER_DOWN); + msleep(20); + sp_block_power_ctrl(anx78xx, SP_TX_PWR_HDCP, SP_POWER_ON); + sp_write_reg(anx78xx, TX_P2, SP_COMMON_INT_MASK2, 0x01); + msleep(50); + sp_tx_hw_hdcp_enable(anx78xx); + sp.hcdp_state = HDCP_WAITTING_FINISH; + /* fallthrough */ + case HDCP_WAITTING_FINISH: + break; + case HDCP_FINISH: + sp_tx_hdcp_encryption_enable(anx78xx); + hdmi_rx_mute_video(anx78xx, 0); + sp_tx_video_mute(anx78xx, 0); + goto_next_system_state(anx78xx); + sp.hcdp_state = HDCP_CAPABLE_CHECK; + dev_dbg(dev, "@@@@@@@hdcp_auth_pass@@@@@@\n"); + break; + case HDCP_FAILED: + if (sp.hdcp_fail_count > 5) { + sp_vbus_power_off(anx78xx); + reg_hardware_reset(anx78xx); + sp.hcdp_state = HDCP_CAPABLE_CHECK; + sp.hdcp_fail_count = 0; + dev_dbg(dev, "*********hdcp_auth_failed*********\n"); + } else { + sp.hdcp_fail_count++; + sp.hcdp_state = HDCP_WAITTING_VID_STB; + } + break; + default: + case HDCP_NOT_SUPPORT: + dev_dbg(dev, "Sink is not capable HDCP\n"); + sp_block_power_ctrl(anx78xx, SP_TX_PWR_HDCP, + SP_POWER_DOWN); + sp_tx_video_mute(anx78xx, 0); + goto_next_system_state(anx78xx); + sp.hcdp_state = HDCP_CAPABLE_CHECK; + break; + } +} + +/******************End HDCP process********************/ + +/******************Start Audio process********************/ +static void sp_tx_audioinfoframe_setup(struct anx78xx *anx78xx) +{ + int i; + u8 regval; + + sp_read_reg(anx78xx, RX_P1, HDMI_RX_AUDIO_TYPE_REG, ®val); + sp.tx_audioinfoframe.type = regval; + sp_read_reg(anx78xx, RX_P1, HDMI_RX_AUDIO_VER_REG, ®val); + sp.tx_audioinfoframe.version = regval; + sp_read_reg(anx78xx, RX_P1, HDMI_RX_AUDIO_LEN_REG, ®val); + sp.tx_audioinfoframe.length = regval; + + for (i = 0; i < 11; i++) { + sp_read_reg(anx78xx, RX_P1, HDMI_RX_AUDIO_DATA00_REG + i, + ®val); + sp.tx_audioinfoframe.pb_byte[i] = regval; + } +} + +static void sp_tx_enable_audio_output(struct anx78xx *anx78xx, u8 enable) +{ + u8 regval; + + sp_read_reg(anx78xx, TX_P0, SP_TX_AUD_CTRL, ®val); + if (enable) { + if (regval & AUD_EN) { + regval &= ~AUD_EN; + sp_write_reg(anx78xx, TX_P0, SP_TX_AUD_CTRL, regval); + } + sp_tx_audioinfoframe_setup(anx78xx); + sp_tx_config_packets(anx78xx, AUDIF_PACKETS); + + regval |= AUD_EN; + sp_write_reg(anx78xx, TX_P0, SP_TX_AUD_CTRL, regval); + } else { + regval &= ~AUD_EN; + sp_write_reg(anx78xx, TX_P0, SP_TX_AUD_CTRL, regval); + sp_write_reg_and(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ~AUD_IF_EN); + } +} + +static void sp_tx_config_audio(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + u8 regval; + int i; + unsigned long m_aud, ls_clk = 0; + unsigned long aud_freq = 0; + + sp_block_power_ctrl(anx78xx, SP_TX_PWR_AUDIO, SP_POWER_ON); + sp_read_reg(anx78xx, RX_P0, AUD_SPDIF_CHST4, ®val); + + switch (regval & 0x0f) { + case FS_FREQ_44100HZ: + aud_freq = 44100; + break; + case FS_FREQ_48000HZ: + aud_freq = 48000; + break; + case FS_FREQ_32000HZ: + aud_freq = 32000; + break; + case FS_FREQ_88200HZ: + aud_freq = 88200; + break; + case FS_FREQ_96000HZ: + aud_freq = 96000; + break; + case FS_FREQ_176400HZ: + aud_freq = 176400; + break; + case FS_FREQ_192000HZ: + aud_freq = 192000; + break; + default: + break; + } + + switch (sp_tx_get_link_bw(anx78xx)) { + case LINK_1P62G: + ls_clk = 162000; + break; + case LINK_2P7G: + ls_clk = 270000; + break; + case LINK_5P4G: + ls_clk = 540000; + break; + case LINK_6P75G: + ls_clk = 675000; + break; + default: + break; + } + + dev_dbg(dev, "aud_freq = %ld , LS_CLK = %ld\n", aud_freq, ls_clk); + + m_aud = ((512 * aud_freq) / ls_clk) * 32768; + m_aud = m_aud + 0x05; + sp_write_reg(anx78xx, TX_P1, SP_TX_AUD_INTERFACE_CTRL4, m_aud & 0xff); + m_aud = m_aud >> 8; + sp_write_reg(anx78xx, TX_P1, SP_TX_AUD_INTERFACE_CTRL5, m_aud & 0xff); + sp_write_reg(anx78xx, TX_P1, SP_TX_AUD_INTERFACE_CTRL6, 0x00); + + sp_write_reg_and(anx78xx, TX_P1, SP_TX_AUD_INTERFACE_CTRL0, + (u8)~AUD_INTERFACE_DISABLE); + + sp_write_reg_or(anx78xx, TX_P1, SP_TX_AUD_INTERFACE_CTRL2, + M_AUD_ADJUST_ST); + + sp_read_reg(anx78xx, RX_P0, HDMI_STATUS, ®val); + if (regval & HDMI_AUD_LAYOUT) + sp_write_reg_or(anx78xx, TX_P2, SP_TX_AUD_CH_NUM_REG5, + CH_NUM_8 | AUD_LAYOUT); + else + sp_write_reg_and(anx78xx, TX_P2, SP_TX_AUD_CH_NUM_REG5, + (u8)~CH_NUM_8 & ~AUD_LAYOUT); + + /* transfer audio chaneel status from HDMI Rx to Slinmport Tx */ + for (i = 0; i < 5; i++) { + sp_read_reg(anx78xx, RX_P0, HDMI_RX_AUD_IN_CH_STATUS1_REG + i, + ®val); + sp_write_reg(anx78xx, TX_P2, SP_TX_AUD_CH_STATUS_REG1 + i, + regval); + } + + /* enable audio */ + sp_tx_enable_audio_output(anx78xx, 1); +} + +static void sp_config_audio_output(struct anx78xx *anx78xx) +{ + static u8 count; + + switch (sp.tx_ao_state) { + default: + case AO_INIT: + case AO_CTS_RCV_INT: + case AO_AUDIO_RCV_INT: + if (!(sp_i2c_read_byte(anx78xx, RX_P0, HDMI_STATUS) + & HDMI_MODE)) { + sp.tx_ao_state = AO_INIT; + goto_next_system_state(anx78xx); + } + break; + case AO_RCV_INT_FINISH: + if (count++ > 2) + sp.tx_ao_state = AO_OUTPUT; + else + sp.tx_ao_state = AO_INIT; + SP_BREAK(AO_INIT, sp.tx_ao_state); + /* fallthrough */ + case AO_OUTPUT: + count = 0; + sp.tx_ao_state = AO_INIT; + hdmi_rx_mute_audio(anx78xx, 0); + sp_tx_config_audio(anx78xx); + goto_next_system_state(anx78xx); + break; + } +} + +/******************End Audio process********************/ + +void sp_initialization(struct anx78xx *anx78xx) +{ + /* Waitting Hot plug event! */ + if (!(sp.common_int_status.common_int[3] & PLUG)) + return; + + sp.read_edid_flag = 0; + + /* Power on all modules */ + sp_write_reg(anx78xx, TX_P2, SP_POWERD_CTRL_REG, 0x00); + /* Driver Version */ + sp_write_reg(anx78xx, TX_P1, FW_VER_REG, FW_VERSION); + hdmi_rx_initialization(anx78xx); + sp_tx_initialization(anx78xx); + msleep(200); + goto_next_system_state(anx78xx); +} + +static void sp_hdcp_external_ctrl_flag_monitor(struct anx78xx *anx78xx) +{ + static u8 cur_flag; + + if (sp.block_en != cur_flag) { + cur_flag = sp.block_en; + system_state_change_with_case(anx78xx, STATE_HDCP_AUTH); + } +} + +static void sp_state_process(struct anx78xx *anx78xx) +{ + switch (sp.tx_system_state) { + case STATE_WAITTING_CABLE_PLUG: + sp_waiting_cable_plug_process(anx78xx); + SP_BREAK(STATE_WAITTING_CABLE_PLUG, sp.tx_system_state); + /* fallthrough */ + case STATE_SP_INITIALIZED: + sp_initialization(anx78xx); + SP_BREAK(STATE_SP_INITIALIZED, sp.tx_system_state); + /* fallthrough */ + case STATE_SINK_CONNECTION: + sp_sink_connection(anx78xx); + SP_BREAK(STATE_SINK_CONNECTION, sp.tx_system_state); + /* fallthrough */ + case STATE_PARSE_EDID: + sp_edid_process(anx78xx); + SP_BREAK(STATE_PARSE_EDID, sp.tx_system_state); + /* fallthrough */ + case STATE_LINK_TRAINING: + sp_link_training(anx78xx); + SP_BREAK(STATE_LINK_TRAINING, sp.tx_system_state); + /* fallthrough */ + case STATE_VIDEO_OUTPUT: + sp_config_video_output(anx78xx); + SP_BREAK(STATE_VIDEO_OUTPUT, sp.tx_system_state); + /* fallthrough */ + case STATE_HDCP_AUTH: + sp_hdcp_process(anx78xx); + SP_BREAK(STATE_HDCP_AUTH, sp.tx_system_state); + /* fallthrough */ + case STATE_AUDIO_OUTPUT: + sp_config_audio_output(anx78xx); + SP_BREAK(STATE_AUDIO_OUTPUT, sp.tx_system_state); + /* fallthrough */ + case STATE_PLAY_BACK: + SP_BREAK(STATE_PLAY_BACK, sp.tx_system_state); + /* fallthrough */ + default: + break; + } +} + +/******************Start INT process********************/ +static void sp_tx_int_rec(struct anx78xx *anx78xx) +{ + sp_read_reg(anx78xx, TX_P2, SP_COMMON_INT_STATUS1, + &sp.common_int_status.common_int[0]); + sp_write_reg(anx78xx, TX_P2, SP_COMMON_INT_STATUS1, + sp.common_int_status.common_int[0]); + + sp_read_reg(anx78xx, TX_P2, SP_COMMON_INT_STATUS1 + 1, + &sp.common_int_status.common_int[1]); + sp_write_reg(anx78xx, TX_P2, SP_COMMON_INT_STATUS1 + 1, + sp.common_int_status.common_int[1]); + + sp_read_reg(anx78xx, TX_P2, SP_COMMON_INT_STATUS1 + 2, + &sp.common_int_status.common_int[2]); + sp_write_reg(anx78xx, TX_P2, SP_COMMON_INT_STATUS1 + 2, + sp.common_int_status.common_int[2]); + + sp_read_reg(anx78xx, TX_P2, SP_COMMON_INT_STATUS1 + 3, + &sp.common_int_status.common_int[3]); + sp_write_reg(anx78xx, TX_P2, SP_COMMON_INT_STATUS1 + 3, + sp.common_int_status.common_int[3]); + + sp_read_reg(anx78xx, TX_P2, SP_COMMON_INT_STATUS1 + 6, + &sp.common_int_status.common_int[4]); + sp_write_reg(anx78xx, TX_P2, SP_COMMON_INT_STATUS1 + 6, + sp.common_int_status.common_int[4]); +} + +static void sp_hdmi_rx_int_rec(struct anx78xx *anx78xx) +{ + sp_read_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS1_REG, + &sp.hdmi_rx_int_status.hdmi_rx_int[0]); + sp_write_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS1_REG, + sp.hdmi_rx_int_status.hdmi_rx_int[0]); + sp_read_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS2_REG, + &sp.hdmi_rx_int_status.hdmi_rx_int[1]); + sp_write_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS2_REG, + sp.hdmi_rx_int_status.hdmi_rx_int[1]); + sp_read_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS3_REG, + &sp.hdmi_rx_int_status.hdmi_rx_int[2]); + sp_write_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS3_REG, + sp.hdmi_rx_int_status.hdmi_rx_int[2]); + sp_read_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS4_REG, + &sp.hdmi_rx_int_status.hdmi_rx_int[3]); + sp_write_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS4_REG, + sp.hdmi_rx_int_status.hdmi_rx_int[3]); + sp_read_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS5_REG, + &sp.hdmi_rx_int_status.hdmi_rx_int[4]); + sp_write_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS5_REG, + sp.hdmi_rx_int_status.hdmi_rx_int[4]); + sp_read_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS6_REG, + &sp.hdmi_rx_int_status.hdmi_rx_int[5]); + sp_write_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS6_REG, + sp.hdmi_rx_int_status.hdmi_rx_int[5]); + sp_read_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS7_REG, + &sp.hdmi_rx_int_status.hdmi_rx_int[6]); + sp_write_reg(anx78xx, RX_P0, HDMI_RX_INT_STATUS7_REG, + sp.hdmi_rx_int_status.hdmi_rx_int[6]); +} + +static void sp_int_rec(struct anx78xx *anx78xx) +{ + sp_tx_int_rec(anx78xx); + sp_hdmi_rx_int_rec(anx78xx); +} + +/******************End INT process********************/ + +/******************Start task process********************/ +static void sp_tx_pll_changed_int_handler(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + + if (sp.tx_system_state >= STATE_LINK_TRAINING) { + if (!sp_tx_get_pll_lock_status(anx78xx)) { + dev_dbg(dev, "PLL:PLL not lock!\n"); + sp_tx_set_sys_state(anx78xx, STATE_LINK_TRAINING); + } + } +} + +static void sp_tx_hdcp_link_chk_fail_handler(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + + system_state_change_with_case(anx78xx, STATE_HDCP_AUTH); + + dev_dbg(dev, "hdcp_link_chk_fail:HDCP Sync lost!\n"); +} + +static void sp_tx_phy_auto_test(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + u8 b_sw; + u8 bytebuf[16]; + + /* DPCD 0x219 TEST_LINK_RATE */ + sp_tx_aux_dpcdread_bytes(anx78xx, 0x0, 0x02, 0x19, 1, bytebuf); + dev_dbg(dev, "DPCD:0x00219 = %.2x\n", bytebuf[0]); + switch (bytebuf[0]) { + case LINK_1P62G: + case LINK_2P7G: + case LINK_5P4G: + case LINK_6P75G: + sp_tx_set_link_bw(anx78xx, bytebuf[0]); + sp.tx_test_bw = bytebuf[0]; + break; + default: + sp_tx_set_link_bw(anx78xx, LINK_6P75G); + sp.tx_test_bw = LINK_6P75G; + break; + } + + /* DPCD 0x248 PHY_TEST_PATTERN */ + sp_tx_aux_dpcdread_bytes(anx78xx, 0x0, 0x02, 0x48, 1, bytebuf); + dev_dbg(dev, "DPCD:0x00248 = %.2x\n", bytebuf[0]); + switch (bytebuf[0]) { + case 0: + break; + case 1: + sp_write_reg(anx78xx, TX_P0, SP_TX_TRAINING_PTN_SET_REG, 0x04); + break; + case 2: + sp_write_reg(anx78xx, TX_P0, SP_TX_TRAINING_PTN_SET_REG, 0x08); + break; + case 3: + sp_write_reg(anx78xx, TX_P0, SP_TX_TRAINING_PTN_SET_REG, 0x0c); + break; + case 4: + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x50, 0xa, + bytebuf); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_TEST_PATTERN_REG0, + bytebuf[0]); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_TEST_PATTERN_REG1, + bytebuf[1]); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_TEST_PATTERN_REG2, + bytebuf[2]); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_TEST_PATTERN_REG3, + bytebuf[3]); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_TEST_PATTERN_REG4, + bytebuf[4]); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_TEST_PATTERN_REG5, + bytebuf[5]); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_TEST_PATTERN_REG6, + bytebuf[6]); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_TEST_PATTERN_REG7, + bytebuf[7]); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_TEST_PATTERN_REG8, + bytebuf[8]); + sp_write_reg(anx78xx, TX_P1, SP_TX_LT_TEST_PATTERN_REG9, + bytebuf[9]); + sp_write_reg(anx78xx, TX_P0, SP_TX_TRAINING_PTN_SET_REG, 0x30); + break; + case 5: + sp_write_reg(anx78xx, TX_P0, ADDR_DP_CEP_TRAINING_CTRL0, 0x00); + sp_write_reg(anx78xx, TX_P0, ADDR_DP_CEP_TRAINING_CTRL1, 0x01); + sp_write_reg(anx78xx, TX_P0, SP_TX_TRAINING_PTN_SET_REG, 0x14); + break; + } + + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x00, 0x03, 1, bytebuf); + dev_dbg(dev, "DPCD:0x00003 = %.2x\n", bytebuf[0]); + if (bytebuf[0] & 0x01) + sp_tx_config_ssc(anx78xx, SSC_DEP_4000PPM); + else + sp_tx_spread_enable(anx78xx, 0); + + /* get swing and emphasis adjust request */ + sp_read_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, &b_sw); + + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x06, 1, bytebuf); + dev_dbg(dev, "DPCD:0x00206 = %.2x\n", bytebuf[0]); + switch (bytebuf[0] & 0x0f) { + case 0x00: + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, + (b_sw & ~TX_SW_SET_MASK) | 0x00); + break; + case 0x01: + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, + (b_sw & ~TX_SW_SET_MASK) | 0x01); + break; + case 0x02: + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, + (b_sw & ~TX_SW_SET_MASK) | 0x02); + break; + case 0x03: + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, + (b_sw & ~TX_SW_SET_MASK) | 0x03); + break; + case 0x04: + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, + (b_sw & ~TX_SW_SET_MASK) | 0x08); + break; + case 0x05: + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, + (b_sw & ~TX_SW_SET_MASK) | 0x09); + break; + case 0x06: + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, + (b_sw & ~TX_SW_SET_MASK) | 0x0a); + break; + case 0x08: + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, + (b_sw & ~TX_SW_SET_MASK) | 0x10); + break; + case 0x09: + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, + (b_sw & ~TX_SW_SET_MASK) | 0x11); + break; + case 0x0c: + sp_write_reg(anx78xx, TX_P0, SP_TX_LT_SET_REG, + (b_sw & ~TX_SW_SET_MASK) | 0x18); + break; + default: + break; + } +} + +static void sp_hpd_irq_process(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + u8 regval; + u8 test_vector; + u8 data_buf[6]; + + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x00, 6, data_buf); + dev_dbg(dev, "+++++++++++++Get HPD IRQ %x\n", (int)data_buf[1]); + + if (data_buf[1] != 0) + sp_tx_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x02, + DPCD_SERVICE_IRQ_VECTOR, 1, + &data_buf[1]); + + /* HDCP IRQ */ + if (data_buf[1] & CP_IRQ) { + if (sp.hcdp_state > HDCP_WAITTING_FINISH || + sp.tx_system_state > STATE_HDCP_AUTH) { + sp_tx_aux_dpcdread_bytes(anx78xx, 0x06, 0x80, 0x29, 1, + ®val); + if (regval & 0x04) { + system_state_change_with_case(anx78xx, + STATE_HDCP_AUTH); + dev_dbg(dev, "IRQ:_______HDCP Sync lost!\n"); + } + } + } + + /* AUTOMATED TEST IRQ */ + if (data_buf[1] & TEST_IRQ) { + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x18, 1, + &test_vector); + + if (test_vector & 0x01) { + sp.tx_test_lt = 1; + + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x19, 1, + ®val); + switch (regval) { + case LINK_1P62G: + case LINK_2P7G: + case LINK_5P4G: + case LINK_6P75G: + sp_tx_set_link_bw(anx78xx, regval); + sp.tx_test_bw = regval; + break; + default: + sp_tx_set_link_bw(anx78xx, LINK_6P75G); + sp.tx_test_bw = LINK_6P75G; + break; + } + + dev_dbg(dev, " test_bw = %.2x\n", sp.tx_test_bw); + + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x60, 1, + ®val); + regval = regval | TEST_ACK; + sp_tx_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x02, 0x60, 1, + ®val); + + dev_dbg(dev, "Set TEST_ACK!\n"); + if (sp.tx_system_state >= STATE_LINK_TRAINING) { + sp.tx_lt_state = LT_INIT; + sp_tx_set_sys_state(anx78xx, + STATE_LINK_TRAINING); + } + dev_dbg(dev, "IRQ:test-LT request!\n"); + } + + if (test_vector & 0x02) { + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x60, 1, + ®val); + regval = regval | TEST_ACK; + sp_tx_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x02, 0x60, 1, + ®val); + } + if (test_vector & 0x04) { + if (sp.tx_system_state > STATE_PARSE_EDID) + sp_tx_set_sys_state(anx78xx, STATE_PARSE_EDID); + sp.tx_test_edid = 1; + dev_dbg(dev, "Test EDID Requested!\n"); + } + + if (test_vector & 0x08) { + sp.tx_test_lt = 1; + + sp_tx_phy_auto_test(anx78xx); + + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x02, 0x60, 1, + ®val); + regval = regval | 0x01; + sp_tx_aux_dpcdwrite_bytes(anx78xx, 0x00, 0x02, 0x60, 1, + ®val); + } + } + + if (sp.tx_system_state > STATE_LINK_TRAINING) { + if (!(data_buf[4] & 0x01) || + ((data_buf[2] & (0x01 | 0x04)) != 0x05)) { + sp_tx_set_sys_state(anx78xx, STATE_LINK_TRAINING); + dev_dbg(dev, "INT:re-LT request!\n"); + return; + } + + dev_dbg(dev, "Lane align %x\n", data_buf[4]); + dev_dbg(dev, "Lane clock recovery %x\n", data_buf[2]); + } +} + +static void sp_tx_vsi_setup(struct anx78xx *anx78xx) +{ + u8 regval; + int i; + + for (i = 0; i < 10; i++) { + sp_read_reg(anx78xx, RX_P1, (HDMI_RX_MPEG_DATA00_REG + i), + ®val); + sp.tx_packet_mpeg.mpeg_data[i] = regval; + } +} + +static void sp_tx_mpeg_setup(struct anx78xx *anx78xx) +{ + u8 regval; + int i; + + for (i = 0; i < 10; i++) { + sp_read_reg(anx78xx, RX_P1, (HDMI_RX_MPEG_DATA00_REG + i), + ®val); + sp.tx_packet_mpeg.mpeg_data[i] = regval; + } +} + +static void sp_tx_auth_done_int_handler(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + u8 bytebuf[2]; + + if (sp.hcdp_state > HDCP_HW_ENABLE && + sp.tx_system_state == STATE_HDCP_AUTH) { + sp_read_reg(anx78xx, TX_P0, SP_TX_HDCP_STATUS, bytebuf); + if (bytebuf[0] & SP_TX_HDCP_AUTH_PASS) { + sp_tx_aux_dpcdread_bytes(anx78xx, 0x06, 0x80, 0x2a, 2, + bytebuf); + if ((bytebuf[1] & 0x08) || (bytebuf[0] & 0x80)) { + dev_dbg(dev, "max cascade/devs exceeded!\n"); + sp_tx_hdcp_encryption_disable(anx78xx); + } else + dev_dbg(dev, "%s\n", + "Authentication pass in Auth_Done"); + + sp.hcdp_state = HDCP_FINISH; + } else { + dev_err(dev, "Authentication failed in AUTH_done\n"); + sp_tx_video_mute(anx78xx, 1); + sp_tx_clean_hdcp_status(anx78xx); + sp.hcdp_state = HDCP_FAILED; + } + } +} + +static void sp_tx_lt_done_int_handler(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + u8 regval; + + if (sp.tx_lt_state == LT_WAITTING_FINISH && + sp.tx_system_state == STATE_LINK_TRAINING) { + sp_read_reg(anx78xx, TX_P0, LT_CTRL, ®val); + if (regval & 0x70) { + regval = (regval & 0x70) >> 4; + dev_dbg(dev, "LT failed in interrupt, ERR = %.2x\n", + regval); + sp.tx_lt_state = LT_ERROR; + } else { + dev_dbg(dev, "lt_done: LT Finish\n"); + sp.tx_lt_state = LT_FINISH; + } + } +} + +static void sp_hdmi_rx_clk_det_int(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + + dev_dbg(dev, "*HDMI_RX Interrupt: Pixel Clock Change.\n"); + if (sp.tx_system_state > STATE_VIDEO_OUTPUT) { + sp_tx_video_mute(anx78xx, 1); + sp_tx_enable_audio_output(anx78xx, 0); + sp_tx_set_sys_state(anx78xx, STATE_VIDEO_OUTPUT); + } +} + +static void sp_hdmi_rx_hdmi_dvi_int(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + u8 regval; + + dev_dbg(dev, "sp_hdmi_rx_hdmi_dvi_int.\n"); + sp_read_reg(anx78xx, RX_P0, HDMI_STATUS, ®val); + sp.hdmi_dvi_status = 1; + if ((regval & BIT(0)) != (sp.hdmi_dvi_status & BIT(0))) { + dev_dbg(dev, "hdmi_dvi_int: Is HDMI MODE: %x.\n", + regval & HDMI_MODE); + sp.hdmi_dvi_status = regval & BIT(0); + hdmi_rx_mute_audio(anx78xx, 1); + system_state_change_with_case(anx78xx, STATE_LINK_TRAINING); + } +} + +static void sp_hdmi_rx_new_avi_int(struct anx78xx *anx78xx) +{ + sp_tx_lvttl_bit_mapping(anx78xx); + sp_tx_set_colorspace(anx78xx); + sp_tx_avi_setup(anx78xx); + sp_tx_config_packets(anx78xx, AVI_PACKETS); +} + +static void sp_hdmi_rx_new_vsi_int(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + u8 hdmi_video_format, v3d_structure; + + sp_write_reg_and(anx78xx, TX_P0, SP_TX_3D_VSC_CTRL, + ~INFO_FRAME_VSC_EN); + + /* VSI package header */ + if ((sp_i2c_read_byte(anx78xx, RX_P1, + HDMI_RX_MPEG_TYPE_REG) != 0x81) || + (sp_i2c_read_byte(anx78xx, RX_P1, HDMI_RX_MPEG_VER_REG) != 0x01)) + return; + + dev_dbg(dev, "Setup VSI package!\n"); + + sp_tx_vsi_setup(anx78xx); + sp_tx_config_packets(anx78xx, VSI_PACKETS); + + sp_read_reg(anx78xx, RX_P1, HDMI_RX_MPEG_DATA03_REG, + &hdmi_video_format); + + if ((hdmi_video_format & 0xe0) == 0x40) { + dev_dbg(dev, "3D VSI packet detected. Config VSC packet\n"); + + sp_read_reg(anx78xx, RX_P1, HDMI_RX_MPEG_DATA05_REG, + &v3d_structure); + + switch (v3d_structure & 0xf0) { + case 0x00: + v3d_structure = 0x02; + break; + case 0x20: + v3d_structure = 0x03; + break; + case 0x30: + v3d_structure = 0x04; + break; + default: + v3d_structure = 0x00; + dev_dbg(dev, "3D structure is not supported\n"); + break; + } + sp_write_reg(anx78xx, TX_P0, SP_TX_VSC_DB1, v3d_structure); + } + sp_write_reg_or(anx78xx, TX_P0, SP_TX_3D_VSC_CTRL, INFO_FRAME_VSC_EN); + sp_write_reg_and(anx78xx, TX_P0, SP_TX_PKT_EN_REG, ~SPD_IF_EN); + sp_write_reg_or(anx78xx, TX_P0, SP_TX_PKT_EN_REG, SPD_IF_UD); + sp_write_reg_or(anx78xx, TX_P0, SP_TX_PKT_EN_REG, SPD_IF_EN); +} + +static void sp_hdmi_rx_no_vsi_int(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + u8 regval; + + sp_read_reg(anx78xx, TX_P0, SP_TX_3D_VSC_CTRL, ®val); + if (regval & INFO_FRAME_VSC_EN) { + dev_dbg(dev, "No new VSI is received, disable VSC packet\n"); + regval &= ~INFO_FRAME_VSC_EN; + sp_write_reg(anx78xx, TX_P0, SP_TX_3D_VSC_CTRL, regval); + sp_tx_mpeg_setup(anx78xx); + sp_tx_config_packets(anx78xx, MPEG_PACKETS); + } +} + +static inline void sp_hdmi_rx_restart_audio_chk(struct anx78xx *anx78xx) +{ + system_state_change_with_case(anx78xx, STATE_AUDIO_OUTPUT); +} + +static void sp_hdmi_rx_cts_rcv_int(struct anx78xx *anx78xx) +{ + if (sp.tx_ao_state == AO_INIT) + sp.tx_ao_state = AO_CTS_RCV_INT; + else if (sp.tx_ao_state == AO_AUDIO_RCV_INT) + sp.tx_ao_state = AO_RCV_INT_FINISH; +} + +static void sp_hdmi_rx_audio_rcv_int(struct anx78xx *anx78xx) +{ + if (sp.tx_ao_state == AO_INIT) + sp.tx_ao_state = AO_AUDIO_RCV_INT; + else if (sp.tx_ao_state == AO_CTS_RCV_INT) + sp.tx_ao_state = AO_RCV_INT_FINISH; +} + +static void sp_hdmi_rx_audio_samplechg_int(struct anx78xx *anx78xx) +{ + u16 i; + u8 regval; + + /* transfer audio chaneel status from HDMI Rx to Slinmport Tx */ + for (i = 0; i < 5; i++) { + sp_read_reg(anx78xx, RX_P0, HDMI_RX_AUD_IN_CH_STATUS1_REG + i, + ®val); + sp_write_reg(anx78xx, TX_P2, SP_TX_AUD_CH_STATUS_REG1 + i, + regval); + } +} + +static void sp_hdmi_rx_hdcp_error_int(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + static u8 count; + + dev_dbg(dev, "*HDMI_RX Interrupt: hdcp error.\n"); + if (count >= 40) { + count = 0; + dev_dbg(dev, "Lots of hdcp error occurred ...\n"); + hdmi_rx_mute_audio(anx78xx, 1); + hdmi_rx_mute_video(anx78xx, 1); + hdmi_rx_set_hpd(anx78xx, 0); + usleep_range(10000, 11000); + hdmi_rx_set_hpd(anx78xx, 1); + } else { + count++; + } +} + +static void sp_hdmi_rx_new_gcp_int(struct anx78xx *anx78xx) +{ + u8 regval; + + sp_read_reg(anx78xx, RX_P1, HDMI_RX_GENERAL_CTRL, ®val); + if (regval & SET_AVMUTE) { + hdmi_rx_mute_video(anx78xx, 1); + hdmi_rx_mute_audio(anx78xx, 1); + } else if (regval & CLEAR_AVMUTE) { + hdmi_rx_mute_video(anx78xx, 0); + hdmi_rx_mute_audio(anx78xx, 0); + } +} + +static void sp_tx_hpd_int_handler(struct anx78xx *anx78xx, u8 hpd_source) +{ + struct device *dev = &anx78xx->client->dev; + + switch (hpd_source) { + case HPD_LOST: + hdmi_rx_set_hpd(anx78xx, 0); + sp_tx_set_sys_state(anx78xx, STATE_WAITTING_CABLE_PLUG); + break; + case HPD_CHANGE: + dev_dbg(dev, "HPD:____________HPD changed!\n"); + usleep_range(2000, 4000); + if (sp.common_int_status.common_int[3] & HPD_IRQ) + sp_hpd_irq_process(anx78xx); + + if (sp_i2c_read_byte(anx78xx, TX_P0, + SP_TX_SYS_CTRL3_REG) & HPD_STATUS) { + if (sp.common_int_status.common_int[3] & HPD_IRQ) + sp_hpd_irq_process(anx78xx); + } else { + if (sp_i2c_read_byte(anx78xx, TX_P0, + SP_TX_SYS_CTRL3_REG) & + HPD_STATUS) { + hdmi_rx_set_hpd(anx78xx, 0); + sp_tx_set_sys_state(anx78xx, + STATE_WAITTING_CABLE_PLUG); + } + } + break; + case PLUG: + dev_dbg(dev, "HPD:____________HPD changed!\n"); + if (sp.tx_system_state < STATE_SP_INITIALIZED) + sp_tx_set_sys_state(anx78xx, STATE_SP_INITIALIZED); + break; + default: + break; + } +} + +static void sp_system_isr_handler(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + + if (sp.common_int_status.common_int[3] & HPD_CHANGE) + sp_tx_hpd_int_handler(anx78xx, HPD_CHANGE); + if (sp.common_int_status.common_int[3] & HPD_LOST) + sp_tx_hpd_int_handler(anx78xx, HPD_LOST); + if (sp.common_int_status.common_int[3] & HPD_IRQ) + dev_dbg(dev, "++++++++++++++++========HDCP_IRQ interrupt\n"); + if (sp.common_int_status.common_int[0] & PLL_LOCK_CHG) + sp_tx_pll_changed_int_handler(anx78xx); + + if (sp.common_int_status.common_int[1] & HDCP_AUTH_DONE) + sp_tx_auth_done_int_handler(anx78xx); + + if (sp.common_int_status.common_int[2] & HDCP_LINK_CHECK_FAIL) + sp_tx_hdcp_link_chk_fail_handler(anx78xx); + + if (sp.common_int_status.common_int[4] & TRAINING_FINISH) + sp_tx_lt_done_int_handler(anx78xx); + + if (sp.tx_system_state > STATE_SINK_CONNECTION) { + if (sp.hdmi_rx_int_status.hdmi_rx_int[5] & NEW_AVI) + sp_hdmi_rx_new_avi_int(anx78xx); + } + + if (sp.tx_system_state > STATE_VIDEO_OUTPUT) { + if (sp.hdmi_rx_int_status.hdmi_rx_int[6] & NEW_VS) { + sp.hdmi_rx_int_status.hdmi_rx_int[6] &= ~NO_VSI; + sp_hdmi_rx_new_vsi_int(anx78xx); + } + if (sp.hdmi_rx_int_status.hdmi_rx_int[6] & NO_VSI) + sp_hdmi_rx_no_vsi_int(anx78xx); + } + + if (sp.tx_system_state >= STATE_VIDEO_OUTPUT) { + if (sp.hdmi_rx_int_status.hdmi_rx_int[0] & CKDT_CHANGE) + sp_hdmi_rx_clk_det_int(anx78xx); + + if (sp.hdmi_rx_int_status.hdmi_rx_int[0] & SCDT_CHANGE) + dev_dbg(dev, "*HDMI_RX Interrupt: Sync Detect.\n"); + + if (sp.hdmi_rx_int_status.hdmi_rx_int[0] & HDMI_DVI) + sp_hdmi_rx_hdmi_dvi_int(anx78xx); + + if ((sp.hdmi_rx_int_status.hdmi_rx_int[5] & NEW_AUD) || + (sp.hdmi_rx_int_status.hdmi_rx_int[2] & AUD_MODE_CHANGE)) + sp_hdmi_rx_restart_audio_chk(anx78xx); + + if (sp.hdmi_rx_int_status.hdmi_rx_int[5] & CTS_RCV) + sp_hdmi_rx_cts_rcv_int(anx78xx); + + if (sp.hdmi_rx_int_status.hdmi_rx_int[4] & AUDIO_RCV) + sp_hdmi_rx_audio_rcv_int(anx78xx); + + if (sp.hdmi_rx_int_status.hdmi_rx_int[1] & HDCP_ERR) + sp_hdmi_rx_hdcp_error_int(anx78xx); + + if (sp.hdmi_rx_int_status.hdmi_rx_int[5] & NEW_CP) + sp_hdmi_rx_new_gcp_int(anx78xx); + + if (sp.hdmi_rx_int_status.hdmi_rx_int[1] & AUDIO_SAMPLE_CHANGE) + sp_hdmi_rx_audio_samplechg_int(anx78xx); + } +} + +static void sp_tx_show_information(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + u8 regval, regval1; + u16 h_res, h_act, v_res, v_act; + u16 h_fp, h_sw, h_bp, v_fp, v_sw, v_bp; + unsigned long fresh_rate; + unsigned long pclk; + + dev_dbg(dev, "\n************* SP Video Information **************\n"); + + switch (sp_tx_get_link_bw(anx78xx)) { + case LINK_1P62G: + dev_dbg(dev, "BW = 1.62G\n"); + break; + case LINK_2P7G: + dev_dbg(dev, "BW = 2.7G\n"); + break; + case LINK_5P4G: + dev_dbg(dev, "BW = 5.4G\n"); + break; + case LINK_6P75G: + dev_dbg(dev, "BW = 6.75G\n"); + break; + default: + break; + } + + pclk = sp_tx_pclk_calc(anx78xx); + pclk = pclk / 10; + + sp_read_reg(anx78xx, TX_P2, SP_TX_TOTAL_LINE_STA_L, ®val); + sp_read_reg(anx78xx, TX_P2, SP_TX_TOTAL_LINE_STA_H, ®val1); + + v_res = regval1; + v_res = v_res << 8; + v_res = v_res + regval; + + sp_read_reg(anx78xx, TX_P2, SP_TX_ACT_LINE_STA_L, ®val); + sp_read_reg(anx78xx, TX_P2, SP_TX_ACT_LINE_STA_H, ®val1); + + v_act = regval1; + v_act = v_act << 8; + v_act = v_act + regval; + + sp_read_reg(anx78xx, TX_P2, SP_TX_TOTAL_PIXEL_STA_L, ®val); + sp_read_reg(anx78xx, TX_P2, SP_TX_TOTAL_PIXEL_STA_H, ®val1); + + h_res = regval1; + h_res = h_res << 8; + h_res = h_res + regval; + + sp_read_reg(anx78xx, TX_P2, SP_TX_ACT_PIXEL_STA_L, ®val); + sp_read_reg(anx78xx, TX_P2, SP_TX_ACT_PIXEL_STA_H, ®val1); + + h_act = regval1; + h_act = h_act << 8; + h_act = h_act + regval; + + sp_read_reg(anx78xx, TX_P2, SP_TX_H_F_PORCH_STA_L, ®val); + sp_read_reg(anx78xx, TX_P2, SP_TX_H_F_PORCH_STA_H, ®val1); + + h_fp = regval1; + h_fp = h_fp << 8; + h_fp = h_fp + regval; + + sp_read_reg(anx78xx, TX_P2, SP_TX_H_SYNC_STA_L, ®val); + sp_read_reg(anx78xx, TX_P2, SP_TX_H_SYNC_STA_H, ®val1); + + h_sw = regval1; + h_sw = h_sw << 8; + h_sw = h_sw + regval; + + sp_read_reg(anx78xx, TX_P2, SP_TX_H_B_PORCH_STA_L, ®val); + sp_read_reg(anx78xx, TX_P2, SP_TX_H_B_PORCH_STA_H, ®val1); + + h_bp = regval1; + h_bp = h_bp << 8; + h_bp = h_bp + regval; + + sp_read_reg(anx78xx, TX_P2, SP_TX_V_F_PORCH_STA, ®val); + v_fp = regval; + + sp_read_reg(anx78xx, TX_P2, SP_TX_V_SYNC_STA, ®val); + v_sw = regval; + + sp_read_reg(anx78xx, TX_P2, SP_TX_V_B_PORCH_STA, ®val); + v_bp = regval; + + dev_dbg(dev, "Total resolution is %d * %d\n", h_res, v_res); + + dev_dbg(dev, "HF=%d, HSW=%d, HBP=%d\n", h_fp, h_sw, h_bp); + dev_dbg(dev, "VF=%d, VSW=%d, VBP=%d\n", v_fp, v_sw, v_bp); + dev_dbg(dev, "Active resolution is %d * %d", h_act, v_act); + + if (h_res == 0 || v_res == 0) { + fresh_rate = 0; + } else { + fresh_rate = pclk * 1000; + fresh_rate = fresh_rate / h_res; + fresh_rate = fresh_rate * 1000; + fresh_rate = fresh_rate / v_res; + } + dev_dbg(dev, " @ %ldHz\n", fresh_rate); + + sp_read_reg(anx78xx, TX_P0, SP_TX_VID_CTRL, ®val); + + if ((regval & 0x06) == 0x00) + dev_dbg(dev, "ColorSpace: RGB,"); + else if ((regval & 0x06) == 0x02) + dev_dbg(dev, "ColorSpace: YCbCr422,"); + else if ((regval & 0x06) == 0x04) + dev_dbg(dev, "ColorSpace: YCbCr444,"); + + sp_read_reg(anx78xx, TX_P0, SP_TX_VID_CTRL, ®val); + + if ((regval & 0xe0) == 0x00) + dev_dbg(dev, "6 BPC\n"); + else if ((regval & 0xe0) == 0x20) + dev_dbg(dev, "8 BPC\n"); + else if ((regval & 0xe0) == 0x40) + dev_dbg(dev, "10 BPC\n"); + else if ((regval & 0xe0) == 0x60) + dev_dbg(dev, "12 BPC\n"); + + if (is_anx_dongle(anx78xx)) { + sp_tx_aux_dpcdread_bytes(anx78xx, 0x00, 0x05, 0x23, 1, ®val); + dev_dbg(dev, "Analogix Dongle FW Ver %.2x\n", regval & 0x7f); + } + + dev_dbg(dev, "\n**************************************************\n"); +} + +static void sp_clean_system_status(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + + if (sp.need_clean_status) { + dev_dbg(dev, "sp_clean_system_status. A -> B;\n"); + dev_dbg(dev, "A:"); + sp_print_system_state(anx78xx, sp.tx_system_state_bak); + dev_dbg(dev, "B:"); + sp_print_system_state(anx78xx, sp.tx_system_state); + + sp.need_clean_status = 0; + if (sp.tx_system_state_bak >= STATE_LINK_TRAINING) { + if (sp.tx_system_state >= STATE_AUDIO_OUTPUT) { + hdmi_rx_mute_audio(anx78xx, 1); + } else { + hdmi_rx_mute_video(anx78xx, 1); + sp_tx_video_mute(anx78xx, 1); + } + } + if (sp.tx_system_state_bak >= STATE_HDCP_AUTH && + sp.tx_system_state <= STATE_HDCP_AUTH) { + if (sp_i2c_read_byte(anx78xx, TX_P0, TX_HDCP_CTRL0) + & 0xfc) + sp_tx_clean_hdcp_status(anx78xx); + } + + if (sp.hcdp_state != HDCP_CAPABLE_CHECK) + sp.hcdp_state = HDCP_CAPABLE_CHECK; + + if (sp.tx_sc_state != SC_INIT) + sp.tx_sc_state = SC_INIT; + if (sp.tx_lt_state != LT_INIT) + sp.tx_lt_state = LT_INIT; + if (sp.tx_vo_state != VO_WAIT_VIDEO_STABLE) + sp.tx_vo_state = VO_WAIT_VIDEO_STABLE; + } +} + +/******************add for HDCP cap check********************/ +static u8 sp_hdcp_cap_check(struct anx78xx *anx78xx) +{ + struct device *dev = &anx78xx->client->dev; + u8 g_hdcp_cap = 0; + u8 value; + + if (sp_tx_aux_dpcdread_bytes(anx78xx, 0x06, 0x80, 0x28, 1, &value) == 0) + g_hdcp_cap = value & 0x01; + else + dev_dbg(dev, "HDCP CAPABLE: read AUX err!\n"); + + dev_dbg(dev, "hdcp cap check: %s Supported\n", + g_hdcp_cap ? "" : "No"); + + return g_hdcp_cap; +} + +/******************End HDCP cap check********************/ + +static void sp_tasks_handler(struct anx78xx *anx78xx) +{ + sp_system_isr_handler(anx78xx); + sp_hdcp_external_ctrl_flag_monitor(anx78xx); + sp_clean_system_status(anx78xx); + /*clear up backup system state*/ + if (sp.tx_system_state_bak != sp.tx_system_state) + sp.tx_system_state_bak = sp.tx_system_state; +} + +/******************End task process********************/ + +void sp_main_process(struct anx78xx *anx78xx) +{ + sp_state_process(anx78xx); + if (sp.tx_system_state > STATE_WAITTING_CABLE_PLUG) { + sp_int_rec(anx78xx); + sp_tasks_handler(anx78xx); + } +} diff --git a/drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.h b/drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.h new file mode 100644 index 0000000..04dbe06 --- /dev/null +++ b/drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.h @@ -0,0 +1,214 @@ +/* + * Copyright(c) 2015, Analogix Semiconductor. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __SLIMPORT_TX_DRV_H +#define __SLIMPORT_TX_DRV_H + +#include "anx78xx.h" +#include "slimport_tx_reg.h" + +#define FW_VERSION 0x22 + +#define DVI_MODE 0x00 +#define HDMI_MODE 0x01 + +#define SP_POWER_ON 1 +#define SP_POWER_DOWN 0 + +#define MAX_BUF_CNT 16 + +#define SP_BREAK(current_status, next_status) \ + { if (next_status != (current_status) + 1) break; } + +enum rx_cbl_type { + DWN_STRM_IS_NULL, + DWN_STRM_IS_HDMI, + DWN_STRM_IS_DIGITAL, + DWN_STRM_IS_ANALOG, + DWN_STRM_NUM +}; + +enum sp_tx_state { + STATE_WAITTING_CABLE_PLUG, + STATE_SP_INITIALIZED, + STATE_SINK_CONNECTION, + STATE_PARSE_EDID, + STATE_LINK_TRAINING, + STATE_VIDEO_OUTPUT, + STATE_HDCP_AUTH, + STATE_AUDIO_OUTPUT, + STATE_PLAY_BACK +}; + +enum sp_tx_power_block { + SP_TX_PWR_REG = REGISTER_PD, + SP_TX_PWR_HDCP = HDCP_PD, + SP_TX_PWR_AUDIO = AUDIO_PD, + SP_TX_PWR_VIDEO = VIDEO_PD, + SP_TX_PWR_LINK = LINK_PD, + SP_TX_PWR_TOTAL = TOTAL_PD, + SP_TX_PWR_NUMS +}; + +enum hdmi_color_depth { + HDMI_LEGACY = 0x00, + HDMI_24BIT = 0x04, + HDMI_30BIT = 0x05, + HDMI_36BIT = 0x06, + HDMI_48BIT = 0x07, +}; + +enum sp_tx_send_msg { + MSG_OCM_EN, + MSG_INPUT_HDMI, + MSG_INPUT_DVI, + MSG_CLEAR_IRQ, +}; + +enum sink_connection_status { + SC_INIT, + SC_CHECK_CABLE_TYPE, + SC_WAITTING_CABLE_TYPE = SC_CHECK_CABLE_TYPE + 5, + SC_SINK_CONNECTED, + SC_NOT_CABLE, + SC_STATE_NUM +}; + +enum cable_type_status { + CHECK_AUXCH, + GETTED_CABLE_TYPE, + CABLE_TYPE_STATE_NUM +}; + +enum sp_tx_lt_status { + LT_INIT, + LT_WAIT_PLL_LOCK, + LT_CHECK_LINK_BW, + LT_START, + LT_WAITTING_FINISH, + LT_ERROR, + LT_FINISH, + LT_END, + LT_STATES_NUM +}; + +enum hdcp_status { + HDCP_CAPABLE_CHECK, + HDCP_WAITTING_VID_STB, + HDCP_HW_ENABLE, + HDCP_WAITTING_FINISH, + HDCP_FINISH, + HDCP_FAILED, + HDCP_NOT_SUPPORT, + HDCP_PROCESS_STATE_NUM +}; + +enum video_output_status { + VO_WAIT_VIDEO_STABLE, + VO_WAIT_TX_VIDEO_STABLE, + VO_CHECK_VIDEO_INFO, + VO_FINISH, + VO_STATE_NUM +}; + +enum audio_output_status { + AO_INIT, + AO_CTS_RCV_INT, + AO_AUDIO_RCV_INT, + AO_RCV_INT_FINISH, + AO_OUTPUT, + AO_STATE_NUM +}; + +struct packet_avi { + u8 avi_data[13]; +}; + +struct packet_spd { + u8 spd_data[25]; +}; + +struct packet_mpeg { + u8 mpeg_data[13]; +}; + +struct audio_info_frame { + u8 type; + u8 version; + u8 length; + u8 pb_byte[11]; +}; + +enum packets_type { + AVI_PACKETS, + SPD_PACKETS, + MPEG_PACKETS, + VSI_PACKETS, + AUDIF_PACKETS +}; + +struct common_int { + u8 common_int[5]; + u8 change_flag; +}; + +struct hdmi_rx_int { + u8 hdmi_rx_int[7]; + u8 change_flag; +}; + +enum xtal_enum { + XTAL_19D2M, + XTAL_24M, + XTAL_25M, + XTAL_26M, + XTAL_27M, + XTAL_38D4M, + XTAL_52M, + XTAL_NOT_SUPPORT, + XTAL_CLK_NUM +}; + +enum sp_ssc_dep { + SSC_DEP_DISABLE = 0x0, + SSC_DEP_500PPM, + SSC_DEP_1000PPM, + SSC_DEP_1500PPM, + SSC_DEP_2000PPM, + SSC_DEP_2500PPM, + SSC_DEP_3000PPM, + SSC_DEP_3500PPM, + SSC_DEP_4000PPM, + SSC_DEP_4500PPM, + SSC_DEP_5000PPM, + SSC_DEP_5500PPM, + SSC_DEP_6000PPM +}; + +struct anx78xx_clock_data { + unsigned char xtal_clk; + unsigned int xtal_clk_m10; +}; + +bool sp_chip_detect(struct anx78xx *anx78xx); + +void sp_main_process(struct anx78xx *anx78xx); + +void sp_tx_variable_init(void); + +enum sp_tx_state sp_tx_current_state(void); + +void sp_tx_clean_state_machine(void); + +#endif diff --git a/drivers/gpu/drm/bridge/anx78xx/slimport_tx_reg.h b/drivers/gpu/drm/bridge/anx78xx/slimport_tx_reg.h new file mode 100644 index 0000000..56b575c --- /dev/null +++ b/drivers/gpu/drm/bridge/anx78xx/slimport_tx_reg.h @@ -0,0 +1,807 @@ +/* + * Copyright(c) 2015, Analogix Semiconductor. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#ifndef __SLIMPORT_TX_REG_DEF_H +#define __SLIMPORT_TX_REG_DEF_H + +#define TX_P0 0x70 +#define TX_P1 0x7a +#define TX_P2 0x72 + +#define RX_P0 0x7e +#define RX_P1 0x80 + +/***************************************************************/ +/* Register definition of device address 0x7e */ +/***************************************************************/ + +#define HDMI_RX_PORT_SEL_REG 0x10 +#define DDC_EN 0x10 +#define TMDS_EN 0x01 + +#define RX_SRST 0x11 +#define VIDEO_RST 0x10 +#define HDCP_MAN_RST 0x04 +#define TMDS_RST 0x02 +#define SW_MAN_RST 0x01 + +#define RX_SW_RST2 0x12 +#define DDC_RST 0x04 + +#define HDMI_RX_SYS_STATUS_REG 0x14 +#define PWR5V 0x08 +#define TMDS_VSYNC_DET 0x04 +#define TMDS_CLOCK_DET 0x02 +#define TMDS_DE_DET 0x01 + +#define HDMI_STATUS 0x15 +#define DEEP_COLOR_MODE 0x40 +#define HDMI_AUD_LAYOUT 0x08 +#define MUTE_STAT 0x04 + +#define RX_MUTE_CTRL 0x16 +#define MUTE_POL 0x04 +#define AUD_MUTE 0x02 +#define VID_MUTE 0x01 + +#define HDMI_RX_SYS_CTRL1_REG 0x17 + +#define RX_SYS_PWDN1 0x18 +#define PWDN_CTRL 0x01 + +#define RX_AEC_CTRL 0x20 +#define AVC_OE 0x80 +#define AAC_OE 0x40 +#define AVC_EN 0x02 +#define AAC_EN 0x01 + +#define RX_AEC_EN0 0x24 +#define AEC_EN07 0x80 +#define AEC_EN06 0x40 +#define AEC_EN05 0x20 +#define AEC_EN04 0x10 +#define AEC_EN03 0x08 +#define AEC_EN02 0x04 +#define AEC_EN01 0x02 +#define AEC_EN00 0x01 + +#define RX_AEC_EN1 0x25 +#define AEC_EN15 0x80 +#define AEC_EN14 0x40 +#define AEC_EN13 0x20 +#define AEC_EN12 0x10 +#define AEC_EN11 0x08 +#define AEC_EN10 0x04 +#define AEC_EN09 0x02 +#define AEC_EN08 0x01 + +#define RX_AEC_EN2 0x26 +#define AEC_EN23 0x80 +#define AEC_EN22 0x40 +#define AEC_EN21 0x20 +#define AEC_EN20 0x10 +#define AEC_EN19 0x08 +#define AEC_EN18 0x04 +#define AEC_EN17 0x02 +#define AEC_EN16 0x01 + +#define HDMI_RX_INT_STATUS1_REG 0x31 +#define HDMI_DVI 0x80 +#define CKDT_CHANGE 0x40 +#define SCDT_CHANGE 0x20 +#define PCLK_CHANGE 0x10 +#define PLL_UNLOCK 0x08 +#define CABLE_UNPLUG 0x04 +#define SET_MUTE 0x02 +#define SW_INTR 0x01 + +#define HDMI_RX_INT_STATUS2_REG 0x32 +#define AUTH_START 0x80 +#define AUTH_DONE 0x40 +#define HDCP_ERR 0x20 +#define ECC_ERR 0x10 +#define AUDIO_SAMPLE_CHANGE 0x01 + +#define HDMI_RX_INT_STATUS3_REG 0x33 +#define AUD_MODE_CHANGE 0x01 + +#define HDMI_RX_INT_STATUS4_REG 0x34 +#define VSYNC_DET 0x80 +#define SYNC_POL_CHANGE 0x40 +#define V_RES_CHANGE 0x20 +#define H_RES_CHANGE 0x10 +#define I_P_CHANGE 0x08 +#define DP_CHANGE 0x04 +#define COLOR_DEPTH_CHANGE 0x02 +#define COLOR_MODE_CHANGE 0x01 + +#define HDMI_RX_INT_STATUS5_REG 0x35 +#define VFIFO_OVERFLOW 0x80 +#define VFIFO_UNDERFLOW 0x40 +#define CTS_N_ERR 0x08 +#define NO_AVI 0x02 +#define AUDIO_RCV 0x01 + +#define HDMI_RX_INT_STATUS6_REG 0x36 +#define CTS_RCV 0x80 +#define NEW_UNR_PKT 0x40 +#define NEW_MPEG 0x20 +#define NEW_AUD 0x10 +#define NEW_SPD 0x08 +#define NEW_ACP 0x04 +#define NEW_AVI 0x02 +#define NEW_CP 0x01 + +#define HDMI_RX_INT_STATUS7_REG 0x37 +#define NO_VSI 0x80 +#define HSYNC_DET 0x20 +#define NEW_VS 0x10 +#define NO_ACP 0x08 +#define REF_CLK_CHG 0x04 +#define CEC_RX_READY 0x02 +#define CEC_TX_DONE 0x01 + +#define HDMI_RX_PKT_RX_INDU_INT_CTRL 0x3f +#define NEW_VS_CTRL 0x80 +#define NEW_UNR 0x40 +#define NEW_MPEG 0x20 +#define NEW_AUD 0x10 +#define NEW_SPD 0x08 +#define NEW_ACP 0x04 +#define NEW_AVI 0x02 + +#define HDMI_RX_INT_MASK1_REG 0x41 +#define HDMI_RX_INT_MASK2_REG 0x42 +#define HDMI_RX_INT_MASK3_REG 0x43 +#define HDMI_RX_INT_MASK4_REG 0x44 +#define HDMI_RX_INT_MASK5_REG 0x45 +#define HDMI_RX_INT_MASK6_REG 0x46 +#define HDMI_RX_INT_MASK7_REG 0x47 + +#define HDMI_RX_TMDS_CTRL_REG1 0x50 +#define HDMI_RX_TMDS_CTRL_REG2 0x51 +#define HDMI_RX_TMDS_CTRL_REG4 0x53 +#define HDMI_RX_TMDS_CTRL_REG5 0x54 +#define HDMI_RX_TMDS_CTRL_REG6 0x55 +#define HDMI_RX_TMDS_CTRL_REG7 0x56 +#define TERM_PD 0x01 + +#define HDMI_RX_TMDS_CTRL_REG18 0x61 +#define PLL_RESET 0x10 + +#define HDMI_RX_TMDS_CTRL_REG19 0x62 +#define HDMI_RX_TMDS_CTRL_REG20 0x63 +#define HDMI_RX_TMDS_CTRL_REG21 0x64 +#define HDMI_RX_TMDS_CTRL_REG22 0x65 + +#define HDMI_RX_VIDEO_STATUS_REG1 0x70 +#define COLOR_DEPTH 0xf0 +#define DEFAULT_PHASE 0x08 +#define VIDEO_TYPE 0x04 + +#define HDMI_RX_HTOTAL_LOW 0x71 +#define HDMI_RX_HTOTAL_HIGH 0x72 +#define HDMI_RX_VTOTAL_LOW 0x73 +#define HDMI_RX_VTOTAL_HIGH 0x74 + +#define HDMI_RX_HACT_LOW 0x75 +#define HDMI_RX_HACT_HIGH 0x76 +#define HDMI_RX_VACT_LOW 0x77 +#define HDMI_RX_VACT_HIGH 0x78 + +#define HDMI_RX_V_SYNC_WIDTH 0x79 +#define HDMI_RX_V_BACK_PORCH 0x7a +#define HDMI_RX_H_FRONT_PORCH_LOW 0x7b +#define HDMI_RX_H_FRONT_PORCH_HIGH 0x7c + +#define HDMI_RX_H_SYNC_WIDTH_LOW 0x7d +#define HDMI_RX_H_SYNC_WIDTH_HIGH 0x7e + +#define RX_VID_DATA_RNG 0x83 +#define YC_LIMT 0x10 +#define OUTPUT_LIMIT_EN 0x08 +#define OUTPUT_LIMIT_RANGE 0x04 +#define R2Y_INPUT_LIMIT 0x02 +#define XVYCC_LIMIT 0x01 + +#define HDMI_RX_VID_OUTPUT_CTRL3_REG 0x86 + +#define HDMI_RX_VID_PCLK_CNTR_REG 0x8b + +/* Pixel Clock High Resolution Counter Register 1 */ +#define PCLK_HR_CNT1 0x8c +/* Pixel Clock High Resolution Counter Register 2 */ +#define PCLK_HR_CNT2 0x8d + +#define HDMI_RX_AUD_IN_CH_STATUS1_REG 0xc7 + +/* Audio in S/PDIF Channel Status Register 4 */ +#define AUD_SPDIF_CHST4 0xca +#define FS_FREQ_44100HZ 0x00 +#define FS_FREQ_48000HZ 0x02 +#define FS_FREQ_32000HZ 0x03 +#define FS_FREQ_88200HZ 0x08 +#define FS_FREQ_96000HZ 0x0a +#define FS_FREQ_176400HZ 0x0c +#define FS_FREQ_192000HZ 0x0e + +#define RX_CEC_CTRL 0xd0 +#define CEC_RX_EN 0x08 +#define CEC_TX_ST 0x04 +#define CEC_PIN_SEL 0x02 +#define CEC_RST 0x01 + +#define HDMI_RX_CEC_RX_STATUS_REG 0xd1 +#define HDMI_RX_CEC_RX_BUSY 0x80 +#define HDMI_RX_CEC_RX_FULL 0x20 +#define HDMI_RX_CEC_RX_EMP 0x10 + +#define HDMI_RX_CEC_TX_STATUS_REG 0xd2 +#define HDMI_RX_CEC_TX_BUSY 0x80 +#define HDMI_RX_CEC_TX_FAIL 0x40 +#define HDMI_RX_CEC_TX_FULL 0x20 +#define HDMI_RX_CEC_TX_EMP 0x10 + +#define HDMI_RX_CEC_FIFO_REG 0xd3 + +#define RX_CEC_SPEED 0xd4 +#define CEC_SPEED_27M 0x40 + +#define HDMI_RX_HDMI_CRITERIA_REG 0xe1 + +#define HDMI_RX_HDCP_EN_CRITERIA_REG 0xe2 +#define ENC_EN_MODE 0x20 + +#define RX_CHIP_CTRL 0xe3 +#define MAN_HDMI5V_DET 0x08 +#define PLLLOCK_CKDT_EN 0x04 +#define ANALOG_CKDT_EN 0x02 +#define DIGITAL_CKDT_EN 0x01 + +#define RX_PACKET_REV_STA 0xf3 +#define AVI_RCVD 0x40 +#define VSI_RCVD 0x20 + +/***************************************************************/ +/* Register definition of device address 0x80 */ +/***************************************************************/ + +#define HDMI_RX_HDCP_STATUS_REG 0x3f +#define ADV_CIPHER 0x80 +#define LOAD_KEY_DONE 0x40 +#define DECRYPT_EN 0x20 +#define AUTH_EN 0x10 +#define BKSV_DISABLE 0x02 +#define CLEAR_RI 0x01 + +#define HDMI_RX_SPD_TYPE_REG 0x40 +#define HDMI_RX_SPD_VER_REG 0x41 +#define HDMI_RX_SPD_LEN_REG 0x42 +#define HDMI_RX_SPD_CHKSUM_REG 0x43 +#define HDMI_RX_SPD_DATA00_REG 0x44 + +#define HDMI_RX_ACP_HB0_REG 0x60 +#define HDMI_RX_ACP_HB1_REG 0x61 +#define HDMI_RX_ACP_HB2_REG 0x62 +#define HDMI_RX_ACP_DATA00_REG 0x63 + +#define HDMI_RX_AVI_TYPE_REG 0xa0 +#define HDMI_RX_AVI_VER_REG 0xa1 +#define HDMI_RX_AVI_LEN_REG 0xa2 +#define HDMI_RX_AVI_CHKSUM_REG 0xa3 +#define HDMI_RX_AVI_DATA00_REG 0xa4 + +#define HDMI_RX_AUDIO_TYPE_REG 0xc0 +#define HDMI_RX_AUDIO_VER_REG 0xc1 +#define HDMI_RX_AUDIO_LEN_REG 0xc2 +#define HDMI_RX_AUDIO_CHKSUM_REG 0xc3 +#define HDMI_RX_AUDIO_DATA00_REG 0xc4 + +#define HDMI_RX_MPEG_TYPE_REG 0xe0 +#define HDMI_RX_MPEG_VER_REG 0xe1 +#define HDMI_RX_MPEG_LEN_REG 0xe2 +#define HDMI_RX_MPEG_CHKSUM_REG 0xe3 +#define HDMI_RX_MPEG_DATA00_REG 0xe4 +#define HDMI_RX_MPEG_DATA03_REG 0xe7 +#define HDMI_RX_MPEG_DATA05_REG 0xe9 + +#define HDMI_RX_SPD_INFO_CTRL 0x5f +#define HDMI_RX_ACP_INFO_CTRL 0x7f + +#define HDMI_RX_GENERAL_CTRL 0x9f +#define CLEAR_AVMUTE 0x10 +#define SET_AVMUTE 0x01 + +#define HDMI_RX_MPEG_VS_CTRL 0xdf +#define HDMI_RX_MPEG_VS_INFO_CTRL 0xff + +/***************************************************************/ +/* Register definition of device address 0x70 */ +/***************************************************************/ + +#define SP_TX_HDCP_STATUS 0x00 +#define SP_TX_HDCP_AUTH_PASS 0x02 + +#define TX_HDCP_CTRL0 0x01 +#define STORE_AN 0x80 +#define RX_REPEATER 0x40 +#define RE_AUTH 0x20 +#define SW_AUTH_OK 0x10 +#define HARD_AUTH_EN 0x08 +#define ENC_EN 0x04 +#define BKSV_SRM_PASS 0x02 +#define KSVLIST_VLD 0x01 + +#define SP_TX_HDCP_CTRL1_REG 0x02 +#define AINFO_EN 0x04 +#define RCV_11_EN 0x02 +#define HDCP_11_EN 0x01 + +#define SP_TX_HDCP_LINK_CHK_FRAME_NUM 0x03 +#define SP_TX_HDCP_CTRL2_REG 0x04 + +#define SP_TX_VID_BLANK_SET1 0x2c +#define SP_TX_VID_BLANK_SET2 0x2d +#define SP_TX_VID_BLANK_SET3 0x2e + +#define SP_TX_WAIT_R0_TIME 0x40 +#define SP_TX_LINK_CHK_TIMER 0x41 +#define SP_TX_WAIT_KSVR_TIME 0x42 + +#define HDCP_KEY_STATUS 0x5e + +#define M_VID_0 0xc0 +#define M_VID_1 0xc1 +#define M_VID_2 0xc2 +#define N_VID_0 0xc3 +#define N_VID_1 0xc4 +#define N_VID_2 0xc5 +#define HDCP_AUTO_TIMER 0x51 +#define HDCP_AUTO_TIMER_VAL 0x00 + +#define HDCP_KEY_CMD 0x5f +#define DISABLE_SYNC_HDCP 0x04 + +#define OTP_KEY_PROTECT1 0x60 +#define OTP_KEY_PROTECT2 0x61 +#define OTP_KEY_PROTECT3 0x62 +#define OTP_PSW1 0xa2 +#define OTP_PSW2 0x7e +#define OTP_PSW3 0xc6 + +#define SP_TX_SYS_CTRL1_REG 0x80 +#define CHIP_AUTH_RESET 0x80 +#define PD_BYPASS_CHIP_AUTH 0x40 +#define DET_STA 0x04 +#define FORCE_DET 0x02 +#define DET_CTRL 0x01 + +#define SP_TX_SYS_CTRL2_REG 0x81 +#define CHA_STA 0x04 +#define FORCE_CHA 0x02 +#define CHA_CTRL 0x01 + +#define SP_TX_SYS_CTRL3_REG 0x82 +#define HPD_STATUS 0x40 +#define F_HPD 0x20 +#define HPD_CTRL 0x10 +#define STRM_VALID 0x04 +#define F_VALID 0x02 +#define VALID_CTRL 0x01 + +#define SP_TX_SYS_CTRL4_REG 0x83 +#define ENHANCED_MODE 0x08 + +#define SP_TX_VID_CTRL 0x84 + +#define SP_TX_AUD_CTRL 0x87 +#define AUD_EN 0x01 + +#define I2C_GEN_10US_TIMER0 0x88 +#define I2C_GEN_10US_TIMER1 0x89 + +#define SP_TX_PKT_EN_REG 0x90 +#define AUD_IF_UP 0x80 +#define AVI_IF_UD 0x40 +#define MPEG_IF_UD 0x20 +#define SPD_IF_UD 0x10 +#define AUD_IF_EN 0x08 +#define AVI_IF_EN 0x04 +#define MPEG_IF_EN 0x02 +#define SPD_IF_EN 0x01 + +#define TX_HDCP_CTRL 0x92 +#define AUTO_EN 0x80 +#define AUTO_START 0x20 +#define LINK_POLLING 0x02 + +#define SP_TX_LINK_BW_SET_REG 0xa0 +#define LINK_BW_SET_MASK 0x0f +#define LINK_6P75G 0x19 +#define LINK_5P4G 0x14 +#define LINK_2P7G 0x0a +#define LINK_1P62G 0x06 + +#define SP_TX_TRAINING_PTN_SET_REG 0xa2 +#define SCRAMBLE_DISABLE 0x20 + +#define SP_TX_LT_SET_REG 0xa3 +#define TX_SW_SET_MASK 0x1b +#define MAX_PRE_REACH 0x20 +#define MAX_DRIVE_REACH 0x04 +#define DRVIE_CURRENT_LEVEL1 0x01 +#define PRE_EMP_LEVEL1 0x08 + +#define LT_CTRL 0xa8 +#define SP_TX_LT_EN 0x01 + +#define ADDR_DP_CEP_TRAINING_CTRL0 0xa9 +#define ADDR_DP_CEP_TRAINING_CTRL1 0xaa +#define ADDR_DP_CEP_TRAINING_CTRL2 0xab + +#define TX_DEBUG1 0xb0 +#define FORCE_HPD 0x80 +#define HPD_POLLING_DET 0x40 +#define HPD_POLLING_EN 0x20 +#define DEBUG_PLL_LOCK 0x10 +#define FORCE_PLL_LOCK 0x08 +#define POLLING_EN 0x02 + +#define SP_TX_DP_POLLING_PERIOD 0xb3 + +#define TX_DP_POLLING 0xb4 +#define AUTO_POLLING_DISABLE 0x01 + +#define TX_LINK_DEBUG 0xb8 +#define M_VID_DEBUG 0x20 +#define NEW_PRBS7 0x10 +#define INSERT_ER 0x02 +#define PRBS31_EN 0x01 + +#define DPCD_200 0xb9 +#define DPCD_201 0xba +#define DPCD_202 0xbb +#define DPCD_203 0xbc +#define DPCD_204 0xbd +#define DPCD_205 0xbe + +#define SP_TX_PLL_CTRL_REG 0xc7 +#define PLL_RST 0x40 + +#define SP_TX_ANALOG_PD_REG 0xc8 +#define MACRO_PD 0x20 +#define AUX_PD 0x10 +#define CH0_PD 0x01 + +#define TX_MISC 0xcd +#define EQ_TRAINING_LOOP 0x40 + +#define SP_TX_DOWN_SPREADING_CTRL1 0xd0 +#define SP_TX_SSC_DISABLE 0xc0 +#define SP_TX_SSC_DWSPREAD 0x40 + +#define SP_TX_M_CALCU_CTRL 0xd9 +#define M_GEN_CLK_SEL 0x01 + +#define TX_EXTRA_ADDR 0xce +#define I2C_STRETCH_DISABLE 0x80 +#define I2C_EXTRA_ADDR 0x50 + +#define SP_TX_AUX_STATUS 0xe0 +#define AUX_BUSY 0x10 + +#define AUX_DEFER_CTRL 0xe2 +#define BUF_DATA_COUNT 0xe4 + +#define AUX_CTRL 0xe5 +#define AUX_ADDR_7_0 0xe6 +#define AUX_ADDR_15_8 0xe7 +#define AUX_ADDR_19_16 0xe8 + +#define AUX_CTRL2 0xe9 +#define ADDR_ONLY_BIT 0x02 +#define AUX_OP_EN 0x01 + +#define SP_TX_3D_VSC_CTRL 0xea +#define INFO_FRAME_VSC_EN 0x01 + +#define SP_TX_VSC_DB1 0xeb + +#define BUF_DATA_0 0xf0 + +/***************************************************************/ +/* Register definition of device address 0x72 */ +/***************************************************************/ + +#define SP_TX_VND_IDL_REG 0x00 +#define SP_TX_VND_IDH_REG 0x01 +#define SP_TX_DEV_IDL_REG 0x02 +#define SP_TX_DEV_IDH_REG 0x03 +#define SP_TX_DEV_REV_REG 0x04 + +#define SP_POWERD_CTRL_REG 0x05 +#define REGISTER_PD 0x80 +#define HDCP_PD 0x20 +#define AUDIO_PD 0x10 +#define VIDEO_PD 0x08 +#define LINK_PD 0x04 +#define TOTAL_PD 0x02 + +#define SP_TX_RST_CTRL_REG 0x06 +#define MISC_RST 0x80 +#define VIDCAP_RST 0x40 +#define VIDFIF_RST 0x20 +#define AUDFIF_RST 0x10 +#define AUDCAP_RST 0x08 +#define HDCP_RST 0x04 +#define SW_RST 0x02 +#define HW_RST 0x01 + +#define RST_CTRL2 0x07 +#define AUX_RST 0x04 +#define SERDES_FIFO_RST 0x02 +#define I2C_REG_RST 0x01 + +#define VID_CTRL1 0x08 +#define VIDEO_EN 0x80 +#define VIDEO_MUTE 0x40 +#define IN_BIT_SEL 0x04 +#define DDR_CTRL 0x02 +#define EDGE_CTRL 0x01 + +#define SP_TX_VID_CTRL2_REG 0x09 +#define IN_BPC_12BIT 0x30 +#define IN_BPC_10BIT 0x20 +#define IN_BPC_8BIT 0x10 + +#define SP_TX_VID_CTRL3_REG 0x0a +#define HPD_OUT 0x40 + +#define SP_TX_VID_CTRL5_REG 0x0c +#define CSC_STD_SEL 0x80 +#define RANGE_Y2R 0x20 +#define CSPACE_Y2R 0x10 + +#define SP_TX_VID_CTRL6_REG 0x0d +#define VIDEO_PROCESS_EN 0x40 +#define UP_SAMPLE 0x02 +#define DOWN_SAMPLE 0x01 + +#define SP_TX_VID_CTRL8_REG 0x0f +#define VID_VRES_TH 0x01 + +#define SP_TX_TOTAL_LINE_STA_L 0x24 +#define SP_TX_TOTAL_LINE_STA_H 0x25 +#define SP_TX_ACT_LINE_STA_L 0x26 +#define SP_TX_ACT_LINE_STA_H 0x27 +#define SP_TX_V_F_PORCH_STA 0x28 +#define SP_TX_V_SYNC_STA 0x29 +#define SP_TX_V_B_PORCH_STA 0x2a +#define SP_TX_TOTAL_PIXEL_STA_L 0x2b +#define SP_TX_TOTAL_PIXEL_STA_H 0x2c +#define SP_TX_ACT_PIXEL_STA_L 0x2d +#define SP_TX_ACT_PIXEL_STA_H 0x2e +#define SP_TX_H_F_PORCH_STA_L 0x2f +#define SP_TX_H_F_PORCH_STA_H 0x30 +#define SP_TX_H_SYNC_STA_L 0x31 +#define SP_TX_H_SYNC_STA_H 0x32 +#define SP_TX_H_B_PORCH_STA_L 0x33 +#define SP_TX_H_B_PORCH_STA_H 0x34 + +#define SP_TX_DP_ADDR_REG1 0x3e + +#define SP_TX_VID_BIT_CTRL0_REG 0x40 +#define SP_TX_VID_BIT_CTRL10_REG 0x4a +#define SP_TX_VID_BIT_CTRL20_REG 0x54 + +#define SP_TX_AVI_TYPE 0x70 +#define SP_TX_AVI_VER 0x71 +#define SP_TX_AVI_LEN 0x72 +#define SP_TX_AVI_DB0 0x73 + +#define BIT_CTRL_SPECIFIC 0x80 +#define ENABLE_BIT_CTRL 0x01 + +#define SP_TX_AUD_TYPE 0x83 +#define SP_TX_AUD_VER 0x84 +#define SP_TX_AUD_LEN 0x85 +#define SP_TX_AUD_DB0 0x86 + +#define SP_TX_SPD_TYPE 0x91 +#define SP_TX_SPD_VER 0x92 +#define SP_TX_SPD_LEN 0x93 +#define SP_TX_SPD_DB0 0x94 + +#define SP_TX_MPEG_TYPE 0xb0 +#define SP_TX_MPEG_VER 0xb1 +#define SP_TX_MPEG_LEN 0xb2 +#define SP_TX_MPEG_DB0 0xb3 + +#define SP_TX_AUD_CH_STATUS_REG1 0xd0 + +#define SP_TX_AUD_CH_NUM_REG5 0xd5 +#define CH_NUM_8 0xe0 +#define AUD_LAYOUT 0x01 + +#define GPIO_1_CONTROL 0xd6 +#define GPIO_1_PULL_UP 0x04 +#define GPIO_1_OEN 0x02 +#define GPIO_1_DATA 0x01 + +#define TX_ANALOG_DEBUG2 0xdd +#define POWERON_TIME_1P5MS 0x03 + +#define TX_PLL_FILTER 0xdf +#define PD_RING_OSC 0x40 +#define V33_SWITCH_ON 0x08 + +#define TX_PLL_FILTER5 0xe0 +#define SP_TX_ANALOG_CTRL0 0xe1 +#define P5V_PROTECT 0x80 +#define SHORT_PROTECT 0x40 +#define P5V_PROTECT_PD 0x20 +#define SHORT_PROTECT_PD 0x10 + +#define TX_ANALOG_CTRL 0xe5 +#define SHORT_DPDM 0x4 + +#define SP_COMMON_INT_STATUS1 0xf1 +#define PLL_LOCK_CHG 0x40 +#define VIDEO_FORMAT_CHG 0x08 +#define AUDIO_CLK_CHG 0x04 +#define VIDEO_CLOCK_CHG 0x02 + +#define SP_COMMON_INT_STATUS2 0xf2 +#define HDCP_AUTH_CHG 0x02 +#define HDCP_AUTH_DONE 0x01 + +#define SP_COMMON_INT_STATUS3 0xf3 +#define HDCP_LINK_CHECK_FAIL 0x01 + +#define SP_COMMON_INT_STATUS4 0xf4 +#define PLUG 0x01 +#define ESYNC_ERR 0x10 +#define HPD_LOST 0x02 +#define HPD_CHANGE 0x04 +#define HPD_IRQ 0x40 + +#define SP_TX_INT_STATUS1 0xf7 +#define DPCD_IRQ_REQUEST 0x80 +#define HPD 0x40 +#define TRAINING_FINISH 0x20 +#define POLLING_ERR 0x10 +#define LINK_CHANGE 0x04 +#define SINK_CHG 0x08 + +#define SP_COMMON_INT_MASK1 0xf8 +#define SP_COMMON_INT_MASK2 0xf9 +#define SP_COMMON_INT_MASK3 0xfa +#define SP_COMMON_INT_MASK4 0xfb +#define SP_INT_MASK 0xfe +#define SP_TX_INT_CTRL_REG 0xff + +/***************************************************************/ +/* Register definition of device address 0x7a */ +/***************************************************************/ + +#define SP_TX_LT_CTRL_REG0 0x30 +#define SP_TX_LT_CTRL_REG1 0x31 +#define SP_TX_LT_CTRL_REG2 0x34 +#define SP_TX_LT_CTRL_REG3 0x35 +#define SP_TX_LT_CTRL_REG4 0x36 +#define SP_TX_LT_CTRL_REG5 0x37 +#define SP_TX_LT_CTRL_REG6 0x38 +#define SP_TX_LT_CTRL_REG7 0x39 +#define SP_TX_LT_CTRL_REG8 0x3a +#define SP_TX_LT_CTRL_REG9 0x3b +#define SP_TX_LT_CTRL_REG10 0x40 +#define SP_TX_LT_CTRL_REG11 0x41 +#define SP_TX_LT_CTRL_REG12 0x44 +#define SP_TX_LT_CTRL_REG13 0x45 +#define SP_TX_LT_CTRL_REG14 0x46 +#define SP_TX_LT_CTRL_REG15 0x47 +#define SP_TX_LT_CTRL_REG16 0x48 +#define SP_TX_LT_CTRL_REG17 0x49 +#define SP_TX_LT_CTRL_REG18 0x4a +#define SP_TX_LT_CTRL_REG19 0x4b +#define SP_TX_LT_TEST_PATTERN_REG0 0x80 +#define SP_TX_LT_TEST_PATTERN_REG1 0x81 +#define SP_TX_LT_TEST_PATTERN_REG2 0x82 +#define SP_TX_LT_TEST_PATTERN_REG3 0x83 +#define SP_TX_LT_TEST_PATTERN_REG4 0x84 +#define SP_TX_LT_TEST_PATTERN_REG5 0x85 +#define SP_TX_LT_TEST_PATTERN_REG6 0x86 +#define SP_TX_LT_TEST_PATTERN_REG7 0x87 +#define SP_TX_LT_TEST_PATTERN_REG8 0x88 +#define SP_TX_LT_TEST_PATTERN_REG9 0x89 + +#define SP_TX_AUD_INTERFACE_CTRL0 0x5f +#define AUD_INTERFACE_DISABLE 0x80 + +#define SP_TX_AUD_INTERFACE_CTRL2 0x60 +#define M_AUD_ADJUST_ST 0x04 + +#define SP_TX_AUD_INTERFACE_CTRL3 0x62 +#define SP_TX_AUD_INTERFACE_CTRL4 0x67 +#define SP_TX_AUD_INTERFACE_CTRL5 0x68 +#define SP_TX_AUD_INTERFACE_CTRL6 0x69 + +#define OCM_REG3 0x96 +#define OCM_RST 0x80 + +#define FW_VER_REG 0xb7 + +/***************************************************************/ +/* Definition of DPCD */ +/***************************************************************/ + +#define DOWN_R_TERM_DET _BIT6 +#define SRAM_EEPROM_LOAD_DONE _BIT5 +#define SRAM_CRC_CHK_DONE _BIT4 +#define SRAM_CRC_CHK_PASS _BIT3 +#define DOWN_STRM_ENC _BIT2 +#define DOWN_STRM_AUTH _BIT1 +#define DOWN_STRM_HPD _BIT0 + +#define DPCD_DPCD_REV 0x00 +#define DPCD_MAX_LINK_RATE 0x01 + +#define DPCD_MAX_LANE_COUNT 0x02 +#define ENHANCED_FRAME_CAP 0x80 + +#define DPCD_MAX_DOWNSPREAD 0x03 +#define DPCD_NORP 0x04 +#define DPCD_DSPORT_PRESENT 0x05 + +#define DPCD_LINK_BW_SET 0x00 +#define DPCD_LANE_COUNT_SET 0x01 +#define ENHANCED_FRAME_EN 0x80 + +#define DPCD_TRAINING_PATTERN_SET 0x02 +#define DPCD_TRAINNIG_LANE0_SET 0x03 + +#define DPCD_DOWNSPREAD_CTRL 0x07 +#define SPREAD_AMPLITUDE 0x10 + +#define DPCD_SINK_COUNT 0x00 +#define DPCD_SERVICE_IRQ_VECTOR 0x01 +#define TEST_IRQ 0x02 +#define CP_IRQ 0x04 +#define SINK_SPECIFIC_IRQ 0x40 + +#define DPCD_LANE0_1_STATUS 0x02 + +#define DPCD_LANE_ALIGN_UD 0x04 +#define DPCD_SINK_STATUS 0x05 + +#define DPCD_TEST_RESPONSE 0x60 +#define TEST_ACK 0x01 +#define DPCD_TEST_EDID_CHECKSUM_WRITE 0x04 + +#define DPCD_TEST_EDID_CHECKSUM 0x61 + +#define DPCD_SPECIFIC_INTERRUPT1 0x10 +#define DPCD_USER_COMM1 0x22 + +#define DPCD_SPECIFIC_INTERRUPT2 0x11 + +#define DPCD_TEST_REQUEST 0x18 +#define DPCD_TEST_LINK_RATE 0x19 + +#define DPCD_TEST_LANE_COUNT 0x20 + +#define DPCD_PHY_TEST_PATTERN 0x48 + +#endif +
At the moment it only supports ANX7814. The ANX7814 is an ultra-low power Full-HD (1080p60) SlimPort transmitter designed for portable devices. This driver adds initial support and supports HDMI to DP pass-through mode. Signed-off-by: Enric Balletbo i Serra <enric.balletbo@collabora.com> --- drivers/gpu/drm/bridge/Kconfig | 2 + drivers/gpu/drm/bridge/Makefile | 1 + drivers/gpu/drm/bridge/anx78xx/Kconfig | 7 + drivers/gpu/drm/bridge/anx78xx/Makefile | 4 + drivers/gpu/drm/bridge/anx78xx/anx78xx.h | 41 + drivers/gpu/drm/bridge/anx78xx/anx78xx_main.c | 228 ++ drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c | 3148 ++++++++++++++++++++++ drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.h | 214 ++ drivers/gpu/drm/bridge/anx78xx/slimport_tx_reg.h | 807 ++++++ 9 files changed, 4452 insertions(+) create mode 100644 drivers/gpu/drm/bridge/anx78xx/Kconfig create mode 100644 drivers/gpu/drm/bridge/anx78xx/Makefile create mode 100644 drivers/gpu/drm/bridge/anx78xx/anx78xx.h create mode 100644 drivers/gpu/drm/bridge/anx78xx/anx78xx_main.c create mode 100644 drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.c create mode 100644 drivers/gpu/drm/bridge/anx78xx/slimport_tx_drv.h create mode 100644 drivers/gpu/drm/bridge/anx78xx/slimport_tx_reg.h