1d5bb994bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later 2c0374eb8SMaciej Purski /* 3c0374eb8SMaciej Purski * Copyright (C) 2017 Samsung Electronics 4c0374eb8SMaciej Purski * 5c0374eb8SMaciej Purski * Authors: 6c0374eb8SMaciej Purski * Tomasz Stanislawski <t.stanislaws@samsung.com> 7c0374eb8SMaciej Purski * Maciej Purski <m.purski@samsung.com> 8c0374eb8SMaciej Purski * 9c0374eb8SMaciej Purski * Based on sii9234 driver created by: 10c0374eb8SMaciej Purski * Adam Hampson <ahampson@sta.samsung.com> 11c0374eb8SMaciej Purski * Erik Gilling <konkers@android.com> 12c0374eb8SMaciej Purski * Shankar Bandal <shankar.b@samsung.com> 13c0374eb8SMaciej Purski * Dharam Kumar <dharam.kr@samsung.com> 14c0374eb8SMaciej Purski */ 15c0374eb8SMaciej Purski #include <drm/bridge/mhl.h> 16ee68c743SBoris Brezillon #include <drm/drm_bridge.h> 17c0374eb8SMaciej Purski #include <drm/drm_crtc.h> 18c0374eb8SMaciej Purski #include <drm/drm_edid.h> 19c0374eb8SMaciej Purski 20c0374eb8SMaciej Purski #include <linux/delay.h> 21c0374eb8SMaciej Purski #include <linux/err.h> 22c0374eb8SMaciej Purski #include <linux/gpio/consumer.h> 23c0374eb8SMaciej Purski #include <linux/i2c.h> 24c0374eb8SMaciej Purski #include <linux/interrupt.h> 25c0374eb8SMaciej Purski #include <linux/irq.h> 26c0374eb8SMaciej Purski #include <linux/kernel.h> 27c0374eb8SMaciej Purski #include <linux/module.h> 28c0374eb8SMaciej Purski #include <linux/mutex.h> 29c0374eb8SMaciej Purski #include <linux/regulator/consumer.h> 30c0374eb8SMaciej Purski #include <linux/slab.h> 31c0374eb8SMaciej Purski 32c0374eb8SMaciej Purski #define CBUS_DEVCAP_OFFSET 0x80 33c0374eb8SMaciej Purski 34c0374eb8SMaciej Purski #define SII9234_MHL_VERSION 0x11 35c0374eb8SMaciej Purski #define SII9234_SCRATCHPAD_SIZE 0x10 36c0374eb8SMaciej Purski #define SII9234_INT_STAT_SIZE 0x33 37c0374eb8SMaciej Purski 38c0374eb8SMaciej Purski #define BIT_TMDS_CCTRL_TMDS_OE BIT(4) 39c0374eb8SMaciej Purski #define MHL_HPD_OUT_OVR_EN BIT(4) 40c0374eb8SMaciej Purski #define MHL_HPD_OUT_OVR_VAL BIT(5) 41c0374eb8SMaciej Purski #define MHL_INIT_TIMEOUT 0x0C 42c0374eb8SMaciej Purski 43c0374eb8SMaciej Purski /* MHL Tx registers and bits */ 44c0374eb8SMaciej Purski #define MHL_TX_SRST 0x05 45c0374eb8SMaciej Purski #define MHL_TX_SYSSTAT_REG 0x09 46c0374eb8SMaciej Purski #define MHL_TX_INTR1_REG 0x71 47c0374eb8SMaciej Purski #define MHL_TX_INTR4_REG 0x74 48c0374eb8SMaciej Purski #define MHL_TX_INTR1_ENABLE_REG 0x75 49c0374eb8SMaciej Purski #define MHL_TX_INTR4_ENABLE_REG 0x78 50c0374eb8SMaciej Purski #define MHL_TX_INT_CTRL_REG 0x79 51c0374eb8SMaciej Purski #define MHL_TX_TMDS_CCTRL 0x80 52c0374eb8SMaciej Purski #define MHL_TX_DISC_CTRL1_REG 0x90 53c0374eb8SMaciej Purski #define MHL_TX_DISC_CTRL2_REG 0x91 54c0374eb8SMaciej Purski #define MHL_TX_DISC_CTRL3_REG 0x92 55c0374eb8SMaciej Purski #define MHL_TX_DISC_CTRL4_REG 0x93 56c0374eb8SMaciej Purski #define MHL_TX_DISC_CTRL5_REG 0x94 57c0374eb8SMaciej Purski #define MHL_TX_DISC_CTRL6_REG 0x95 58c0374eb8SMaciej Purski #define MHL_TX_DISC_CTRL7_REG 0x96 59c0374eb8SMaciej Purski #define MHL_TX_DISC_CTRL8_REG 0x97 60c0374eb8SMaciej Purski #define MHL_TX_STAT2_REG 0x99 61c0374eb8SMaciej Purski #define MHL_TX_MHLTX_CTL1_REG 0xA0 62c0374eb8SMaciej Purski #define MHL_TX_MHLTX_CTL2_REG 0xA1 63c0374eb8SMaciej Purski #define MHL_TX_MHLTX_CTL4_REG 0xA3 64c0374eb8SMaciej Purski #define MHL_TX_MHLTX_CTL6_REG 0xA5 65c0374eb8SMaciej Purski #define MHL_TX_MHLTX_CTL7_REG 0xA6 66c0374eb8SMaciej Purski 67c0374eb8SMaciej Purski #define RSEN_STATUS BIT(2) 68c0374eb8SMaciej Purski #define HPD_CHANGE_INT BIT(6) 69c0374eb8SMaciej Purski #define RSEN_CHANGE_INT BIT(5) 70c0374eb8SMaciej Purski #define RGND_READY_INT BIT(6) 71c0374eb8SMaciej Purski #define VBUS_LOW_INT BIT(5) 72c0374eb8SMaciej Purski #define CBUS_LKOUT_INT BIT(4) 73c0374eb8SMaciej Purski #define MHL_DISC_FAIL_INT BIT(3) 74c0374eb8SMaciej Purski #define MHL_EST_INT BIT(2) 75c0374eb8SMaciej Purski #define HPD_CHANGE_INT_MASK BIT(6) 76c0374eb8SMaciej Purski #define RSEN_CHANGE_INT_MASK BIT(5) 77c0374eb8SMaciej Purski 78c0374eb8SMaciej Purski #define RGND_READY_MASK BIT(6) 79c0374eb8SMaciej Purski #define CBUS_LKOUT_MASK BIT(4) 80c0374eb8SMaciej Purski #define MHL_DISC_FAIL_MASK BIT(3) 81c0374eb8SMaciej Purski #define MHL_EST_MASK BIT(2) 82c0374eb8SMaciej Purski 83c0374eb8SMaciej Purski #define SKIP_GND BIT(6) 84c0374eb8SMaciej Purski 85c0374eb8SMaciej Purski #define ATT_THRESH_SHIFT 0x04 86c0374eb8SMaciej Purski #define ATT_THRESH_MASK (0x03 << ATT_THRESH_SHIFT) 87c0374eb8SMaciej Purski #define USB_D_OEN BIT(3) 88c0374eb8SMaciej Purski #define DEGLITCH_TIME_MASK 0x07 89c0374eb8SMaciej Purski #define DEGLITCH_TIME_2MS 0 90c0374eb8SMaciej Purski #define DEGLITCH_TIME_4MS 1 91c0374eb8SMaciej Purski #define DEGLITCH_TIME_8MS 2 92c0374eb8SMaciej Purski #define DEGLITCH_TIME_16MS 3 93c0374eb8SMaciej Purski #define DEGLITCH_TIME_40MS 4 94c0374eb8SMaciej Purski #define DEGLITCH_TIME_50MS 5 95c0374eb8SMaciej Purski #define DEGLITCH_TIME_60MS 6 96c0374eb8SMaciej Purski #define DEGLITCH_TIME_128MS 7 97c0374eb8SMaciej Purski 98c0374eb8SMaciej Purski #define USB_D_OVR BIT(7) 99c0374eb8SMaciej Purski #define USB_ID_OVR BIT(6) 100c0374eb8SMaciej Purski #define DVRFLT_SEL BIT(5) 101c0374eb8SMaciej Purski #define BLOCK_RGND_INT BIT(4) 102c0374eb8SMaciej Purski #define SKIP_DEG BIT(3) 103c0374eb8SMaciej Purski #define CI2CA_POL BIT(2) 104c0374eb8SMaciej Purski #define CI2CA_WKUP BIT(1) 105c0374eb8SMaciej Purski #define SINGLE_ATT BIT(0) 106c0374eb8SMaciej Purski 107c0374eb8SMaciej Purski #define USB_D_ODN BIT(5) 108c0374eb8SMaciej Purski #define VBUS_CHECK BIT(2) 109c0374eb8SMaciej Purski #define RGND_INTP_MASK 0x03 110c0374eb8SMaciej Purski #define RGND_INTP_OPEN 0 111c0374eb8SMaciej Purski #define RGND_INTP_2K 1 112c0374eb8SMaciej Purski #define RGND_INTP_1K 2 113c0374eb8SMaciej Purski #define RGND_INTP_SHORT 3 114c0374eb8SMaciej Purski 115c0374eb8SMaciej Purski /* HDMI registers */ 116c0374eb8SMaciej Purski #define HDMI_RX_TMDS0_CCTRL1_REG 0x10 117c0374eb8SMaciej Purski #define HDMI_RX_TMDS_CLK_EN_REG 0x11 118c0374eb8SMaciej Purski #define HDMI_RX_TMDS_CH_EN_REG 0x12 119c0374eb8SMaciej Purski #define HDMI_RX_PLL_CALREFSEL_REG 0x17 120c0374eb8SMaciej Purski #define HDMI_RX_PLL_VCOCAL_REG 0x1A 121c0374eb8SMaciej Purski #define HDMI_RX_EQ_DATA0_REG 0x22 122c0374eb8SMaciej Purski #define HDMI_RX_EQ_DATA1_REG 0x23 123c0374eb8SMaciej Purski #define HDMI_RX_EQ_DATA2_REG 0x24 124c0374eb8SMaciej Purski #define HDMI_RX_EQ_DATA3_REG 0x25 125c0374eb8SMaciej Purski #define HDMI_RX_EQ_DATA4_REG 0x26 126c0374eb8SMaciej Purski #define HDMI_RX_TMDS_ZONE_CTRL_REG 0x4C 127c0374eb8SMaciej Purski #define HDMI_RX_TMDS_MODE_CTRL_REG 0x4D 128c0374eb8SMaciej Purski 129c0374eb8SMaciej Purski /* CBUS registers */ 130c0374eb8SMaciej Purski #define CBUS_INT_STATUS_1_REG 0x08 131c0374eb8SMaciej Purski #define CBUS_INTR1_ENABLE_REG 0x09 132c0374eb8SMaciej Purski #define CBUS_MSC_REQ_ABORT_REASON_REG 0x0D 133c0374eb8SMaciej Purski #define CBUS_INT_STATUS_2_REG 0x1E 134c0374eb8SMaciej Purski #define CBUS_INTR2_ENABLE_REG 0x1F 135c0374eb8SMaciej Purski #define CBUS_LINK_CONTROL_2_REG 0x31 136c0374eb8SMaciej Purski #define CBUS_MHL_STATUS_REG_0 0xB0 137c0374eb8SMaciej Purski #define CBUS_MHL_STATUS_REG_1 0xB1 138c0374eb8SMaciej Purski 139c0374eb8SMaciej Purski #define BIT_CBUS_RESET BIT(3) 140c0374eb8SMaciej Purski #define SET_HPD_DOWNSTREAM BIT(6) 141c0374eb8SMaciej Purski 142c0374eb8SMaciej Purski /* TPI registers */ 143c0374eb8SMaciej Purski #define TPI_DPD_REG 0x3D 144c0374eb8SMaciej Purski 145c0374eb8SMaciej Purski /* Timeouts in msec */ 146c0374eb8SMaciej Purski #define T_SRC_VBUS_CBUS_TO_STABLE 200 147c0374eb8SMaciej Purski #define T_SRC_CBUS_FLOAT 100 148c0374eb8SMaciej Purski #define T_SRC_CBUS_DEGLITCH 2 149c0374eb8SMaciej Purski #define T_SRC_RXSENSE_DEGLITCH 110 150c0374eb8SMaciej Purski 151c0374eb8SMaciej Purski #define MHL1_MAX_CLK 75000 /* in kHz */ 152c0374eb8SMaciej Purski 153c0374eb8SMaciej Purski #define I2C_TPI_ADDR 0x3D 154c0374eb8SMaciej Purski #define I2C_HDMI_ADDR 0x49 155c0374eb8SMaciej Purski #define I2C_CBUS_ADDR 0x64 156c0374eb8SMaciej Purski 157c0374eb8SMaciej Purski enum sii9234_state { 158c0374eb8SMaciej Purski ST_OFF, 159c0374eb8SMaciej Purski ST_D3, 160c0374eb8SMaciej Purski ST_RGND_INIT, 161c0374eb8SMaciej Purski ST_RGND_1K, 162c0374eb8SMaciej Purski ST_RSEN_HIGH, 163c0374eb8SMaciej Purski ST_MHL_ESTABLISHED, 164c0374eb8SMaciej Purski ST_FAILURE_DISCOVERY, 165c0374eb8SMaciej Purski ST_FAILURE, 166c0374eb8SMaciej Purski }; 167c0374eb8SMaciej Purski 168c0374eb8SMaciej Purski struct sii9234 { 169c0374eb8SMaciej Purski struct i2c_client *client[4]; 170c0374eb8SMaciej Purski struct drm_bridge bridge; 171c0374eb8SMaciej Purski struct device *dev; 172c0374eb8SMaciej Purski struct gpio_desc *gpio_reset; 173c0374eb8SMaciej Purski int i2c_error; 174c0374eb8SMaciej Purski struct regulator_bulk_data supplies[4]; 175c0374eb8SMaciej Purski 176c0374eb8SMaciej Purski struct mutex lock; /* Protects fields below and device registers */ 177c0374eb8SMaciej Purski enum sii9234_state state; 178c0374eb8SMaciej Purski }; 179c0374eb8SMaciej Purski 180c0374eb8SMaciej Purski enum sii9234_client_id { 181c0374eb8SMaciej Purski I2C_MHL, 182c0374eb8SMaciej Purski I2C_TPI, 183c0374eb8SMaciej Purski I2C_HDMI, 184c0374eb8SMaciej Purski I2C_CBUS, 185c0374eb8SMaciej Purski }; 186c0374eb8SMaciej Purski 187c0374eb8SMaciej Purski static const char * const sii9234_client_name[] = { 188c0374eb8SMaciej Purski [I2C_MHL] = "MHL", 189c0374eb8SMaciej Purski [I2C_TPI] = "TPI", 190c0374eb8SMaciej Purski [I2C_HDMI] = "HDMI", 191c0374eb8SMaciej Purski [I2C_CBUS] = "CBUS", 192c0374eb8SMaciej Purski }; 193c0374eb8SMaciej Purski 194c0374eb8SMaciej Purski static int sii9234_writeb(struct sii9234 *ctx, int id, int offset, 195c0374eb8SMaciej Purski int value) 196c0374eb8SMaciej Purski { 197c0374eb8SMaciej Purski int ret; 198c0374eb8SMaciej Purski struct i2c_client *client = ctx->client[id]; 199c0374eb8SMaciej Purski 200c0374eb8SMaciej Purski if (ctx->i2c_error) 201c0374eb8SMaciej Purski return ctx->i2c_error; 202c0374eb8SMaciej Purski 203c0374eb8SMaciej Purski ret = i2c_smbus_write_byte_data(client, offset, value); 204c0374eb8SMaciej Purski if (ret < 0) 205c0374eb8SMaciej Purski dev_err(ctx->dev, "writeb: %4s[0x%02x] <- 0x%02x\n", 206c0374eb8SMaciej Purski sii9234_client_name[id], offset, value); 207c0374eb8SMaciej Purski ctx->i2c_error = ret; 208c0374eb8SMaciej Purski 209c0374eb8SMaciej Purski return ret; 210c0374eb8SMaciej Purski } 211c0374eb8SMaciej Purski 212c0374eb8SMaciej Purski static int sii9234_writebm(struct sii9234 *ctx, int id, int offset, 213c0374eb8SMaciej Purski int value, int mask) 214c0374eb8SMaciej Purski { 215c0374eb8SMaciej Purski int ret; 216c0374eb8SMaciej Purski struct i2c_client *client = ctx->client[id]; 217c0374eb8SMaciej Purski 218c0374eb8SMaciej Purski if (ctx->i2c_error) 219c0374eb8SMaciej Purski return ctx->i2c_error; 220c0374eb8SMaciej Purski 221c0374eb8SMaciej Purski ret = i2c_smbus_write_byte(client, offset); 222c0374eb8SMaciej Purski if (ret < 0) { 223c0374eb8SMaciej Purski dev_err(ctx->dev, "writebm: %4s[0x%02x] <- 0x%02x\n", 224c0374eb8SMaciej Purski sii9234_client_name[id], offset, value); 225c0374eb8SMaciej Purski ctx->i2c_error = ret; 226c0374eb8SMaciej Purski return ret; 227c0374eb8SMaciej Purski } 228c0374eb8SMaciej Purski 229c0374eb8SMaciej Purski ret = i2c_smbus_read_byte(client); 230c0374eb8SMaciej Purski if (ret < 0) { 231c0374eb8SMaciej Purski dev_err(ctx->dev, "writebm: %4s[0x%02x] <- 0x%02x\n", 232c0374eb8SMaciej Purski sii9234_client_name[id], offset, value); 233c0374eb8SMaciej Purski ctx->i2c_error = ret; 234c0374eb8SMaciej Purski return ret; 235c0374eb8SMaciej Purski } 236c0374eb8SMaciej Purski 237c0374eb8SMaciej Purski value = (value & mask) | (ret & ~mask); 238c0374eb8SMaciej Purski 239c0374eb8SMaciej Purski ret = i2c_smbus_write_byte_data(client, offset, value); 240c0374eb8SMaciej Purski if (ret < 0) { 241c0374eb8SMaciej Purski dev_err(ctx->dev, "writebm: %4s[0x%02x] <- 0x%02x\n", 242c0374eb8SMaciej Purski sii9234_client_name[id], offset, value); 243c0374eb8SMaciej Purski ctx->i2c_error = ret; 244c0374eb8SMaciej Purski } 245c0374eb8SMaciej Purski 246c0374eb8SMaciej Purski return ret; 247c0374eb8SMaciej Purski } 248c0374eb8SMaciej Purski 249c0374eb8SMaciej Purski static int sii9234_readb(struct sii9234 *ctx, int id, int offset) 250c0374eb8SMaciej Purski { 251c0374eb8SMaciej Purski int ret; 252c0374eb8SMaciej Purski struct i2c_client *client = ctx->client[id]; 253c0374eb8SMaciej Purski 254c0374eb8SMaciej Purski if (ctx->i2c_error) 255c0374eb8SMaciej Purski return ctx->i2c_error; 256c0374eb8SMaciej Purski 257c0374eb8SMaciej Purski ret = i2c_smbus_write_byte(client, offset); 258c0374eb8SMaciej Purski if (ret < 0) { 259c0374eb8SMaciej Purski dev_err(ctx->dev, "readb: %4s[0x%02x]\n", 260c0374eb8SMaciej Purski sii9234_client_name[id], offset); 261c0374eb8SMaciej Purski ctx->i2c_error = ret; 262c0374eb8SMaciej Purski return ret; 263c0374eb8SMaciej Purski } 264c0374eb8SMaciej Purski 265c0374eb8SMaciej Purski ret = i2c_smbus_read_byte(client); 266c0374eb8SMaciej Purski if (ret < 0) { 267c0374eb8SMaciej Purski dev_err(ctx->dev, "readb: %4s[0x%02x]\n", 268c0374eb8SMaciej Purski sii9234_client_name[id], offset); 269c0374eb8SMaciej Purski ctx->i2c_error = ret; 270c0374eb8SMaciej Purski } 271c0374eb8SMaciej Purski 272c0374eb8SMaciej Purski return ret; 273c0374eb8SMaciej Purski } 274c0374eb8SMaciej Purski 275c0374eb8SMaciej Purski static int sii9234_clear_error(struct sii9234 *ctx) 276c0374eb8SMaciej Purski { 277c0374eb8SMaciej Purski int ret = ctx->i2c_error; 278c0374eb8SMaciej Purski 279c0374eb8SMaciej Purski ctx->i2c_error = 0; 280c0374eb8SMaciej Purski 281c0374eb8SMaciej Purski return ret; 282c0374eb8SMaciej Purski } 283c0374eb8SMaciej Purski 284c0374eb8SMaciej Purski #define mhl_tx_writeb(sii9234, offset, value) \ 285c0374eb8SMaciej Purski sii9234_writeb(sii9234, I2C_MHL, offset, value) 286c0374eb8SMaciej Purski #define mhl_tx_writebm(sii9234, offset, value, mask) \ 287c0374eb8SMaciej Purski sii9234_writebm(sii9234, I2C_MHL, offset, value, mask) 288c0374eb8SMaciej Purski #define mhl_tx_readb(sii9234, offset) \ 289c0374eb8SMaciej Purski sii9234_readb(sii9234, I2C_MHL, offset) 290c0374eb8SMaciej Purski #define cbus_writeb(sii9234, offset, value) \ 291c0374eb8SMaciej Purski sii9234_writeb(sii9234, I2C_CBUS, offset, value) 292c0374eb8SMaciej Purski #define cbus_writebm(sii9234, offset, value, mask) \ 293c0374eb8SMaciej Purski sii9234_writebm(sii9234, I2C_CBUS, offset, value, mask) 294c0374eb8SMaciej Purski #define cbus_readb(sii9234, offset) \ 295c0374eb8SMaciej Purski sii9234_readb(sii9234, I2C_CBUS, offset) 296c0374eb8SMaciej Purski #define hdmi_writeb(sii9234, offset, value) \ 297c0374eb8SMaciej Purski sii9234_writeb(sii9234, I2C_HDMI, offset, value) 298c0374eb8SMaciej Purski #define hdmi_writebm(sii9234, offset, value, mask) \ 299c0374eb8SMaciej Purski sii9234_writebm(sii9234, I2C_HDMI, offset, value, mask) 300c0374eb8SMaciej Purski #define hdmi_readb(sii9234, offset) \ 301c0374eb8SMaciej Purski sii9234_readb(sii9234, I2C_HDMI, offset) 302c0374eb8SMaciej Purski #define tpi_writeb(sii9234, offset, value) \ 303c0374eb8SMaciej Purski sii9234_writeb(sii9234, I2C_TPI, offset, value) 304c0374eb8SMaciej Purski #define tpi_writebm(sii9234, offset, value, mask) \ 305c0374eb8SMaciej Purski sii9234_writebm(sii9234, I2C_TPI, offset, value, mask) 306c0374eb8SMaciej Purski #define tpi_readb(sii9234, offset) \ 307c0374eb8SMaciej Purski sii9234_readb(sii9234, I2C_TPI, offset) 308c0374eb8SMaciej Purski 309c0374eb8SMaciej Purski static u8 sii9234_tmds_control(struct sii9234 *ctx, bool enable) 310c0374eb8SMaciej Purski { 311c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_TMDS_CCTRL, enable ? ~0 : 0, 312c0374eb8SMaciej Purski BIT_TMDS_CCTRL_TMDS_OE); 313c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_INT_CTRL_REG, enable ? ~0 : 0, 314c0374eb8SMaciej Purski MHL_HPD_OUT_OVR_EN | MHL_HPD_OUT_OVR_VAL); 315c0374eb8SMaciej Purski return sii9234_clear_error(ctx); 316c0374eb8SMaciej Purski } 317c0374eb8SMaciej Purski 318c0374eb8SMaciej Purski static int sii9234_cbus_reset(struct sii9234 *ctx) 319c0374eb8SMaciej Purski { 320c0374eb8SMaciej Purski int i; 321c0374eb8SMaciej Purski 322c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_SRST, ~0, BIT_CBUS_RESET); 323c0374eb8SMaciej Purski msleep(T_SRC_CBUS_DEGLITCH); 324c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_SRST, 0, BIT_CBUS_RESET); 325c0374eb8SMaciej Purski 326c0374eb8SMaciej Purski for (i = 0; i < 4; i++) { 327c0374eb8SMaciej Purski /* 328c0374eb8SMaciej Purski * Enable WRITE_STAT interrupt for writes to all 329c0374eb8SMaciej Purski * 4 MSC Status registers. 330c0374eb8SMaciej Purski */ 331c0374eb8SMaciej Purski cbus_writeb(ctx, 0xE0 + i, 0xF2); 332c0374eb8SMaciej Purski /* 333c0374eb8SMaciej Purski * Enable SET_INT interrupt for writes to all 334c0374eb8SMaciej Purski * 4 MSC Interrupt registers. 335c0374eb8SMaciej Purski */ 336c0374eb8SMaciej Purski cbus_writeb(ctx, 0xF0 + i, 0xF2); 337c0374eb8SMaciej Purski } 338c0374eb8SMaciej Purski 339c0374eb8SMaciej Purski return sii9234_clear_error(ctx); 340c0374eb8SMaciej Purski } 341c0374eb8SMaciej Purski 342c0374eb8SMaciej Purski /* Require to chek mhl imformation of samsung in cbus_init_register */ 343c0374eb8SMaciej Purski static int sii9234_cbus_init(struct sii9234 *ctx) 344c0374eb8SMaciej Purski { 345c0374eb8SMaciej Purski cbus_writeb(ctx, 0x07, 0xF2); 346c0374eb8SMaciej Purski cbus_writeb(ctx, 0x40, 0x03); 347c0374eb8SMaciej Purski cbus_writeb(ctx, 0x42, 0x06); 348c0374eb8SMaciej Purski cbus_writeb(ctx, 0x36, 0x0C); 349c0374eb8SMaciej Purski cbus_writeb(ctx, 0x3D, 0xFD); 350c0374eb8SMaciej Purski cbus_writeb(ctx, 0x1C, 0x01); 351c0374eb8SMaciej Purski cbus_writeb(ctx, 0x1D, 0x0F); 352c0374eb8SMaciej Purski cbus_writeb(ctx, 0x44, 0x02); 353c0374eb8SMaciej Purski /* Setup our devcap */ 354c0374eb8SMaciej Purski cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_DEV_STATE, 0x00); 355c0374eb8SMaciej Purski cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_MHL_VERSION, 356c0374eb8SMaciej Purski SII9234_MHL_VERSION); 357c0374eb8SMaciej Purski cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_CAT, 358c0374eb8SMaciej Purski MHL_DCAP_CAT_SOURCE); 359c0374eb8SMaciej Purski cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_ADOPTER_ID_H, 0x01); 360c0374eb8SMaciej Purski cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_ADOPTER_ID_L, 0x41); 361c0374eb8SMaciej Purski cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_VID_LINK_MODE, 362c0374eb8SMaciej Purski MHL_DCAP_VID_LINK_RGB444 | MHL_DCAP_VID_LINK_YCBCR444); 363c0374eb8SMaciej Purski cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_VIDEO_TYPE, 364c0374eb8SMaciej Purski MHL_DCAP_VT_GRAPHICS); 365c0374eb8SMaciej Purski cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_LOG_DEV_MAP, 366c0374eb8SMaciej Purski MHL_DCAP_LD_GUI); 367c0374eb8SMaciej Purski cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_BANDWIDTH, 0x0F); 368c0374eb8SMaciej Purski cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_FEATURE_FLAG, 369c0374eb8SMaciej Purski MHL_DCAP_FEATURE_RCP_SUPPORT | MHL_DCAP_FEATURE_RAP_SUPPORT 370c0374eb8SMaciej Purski | MHL_DCAP_FEATURE_SP_SUPPORT); 371c0374eb8SMaciej Purski cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_DEVICE_ID_H, 0x0); 372c0374eb8SMaciej Purski cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_DEVICE_ID_L, 0x0); 373c0374eb8SMaciej Purski cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_SCRATCHPAD_SIZE, 374c0374eb8SMaciej Purski SII9234_SCRATCHPAD_SIZE); 375c0374eb8SMaciej Purski cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_INT_STAT_SIZE, 376c0374eb8SMaciej Purski SII9234_INT_STAT_SIZE); 377c0374eb8SMaciej Purski cbus_writeb(ctx, CBUS_DEVCAP_OFFSET + MHL_DCAP_RESERVED, 0); 378c0374eb8SMaciej Purski cbus_writebm(ctx, 0x31, 0x0C, 0x0C); 379c0374eb8SMaciej Purski cbus_writeb(ctx, 0x30, 0x01); 380c0374eb8SMaciej Purski cbus_writebm(ctx, 0x3C, 0x30, 0x38); 381c0374eb8SMaciej Purski cbus_writebm(ctx, 0x22, 0x0D, 0x0F); 382c0374eb8SMaciej Purski cbus_writebm(ctx, 0x2E, 0x15, 0x15); 383c0374eb8SMaciej Purski cbus_writeb(ctx, CBUS_INTR1_ENABLE_REG, 0); 384c0374eb8SMaciej Purski cbus_writeb(ctx, CBUS_INTR2_ENABLE_REG, 0); 385c0374eb8SMaciej Purski 386c0374eb8SMaciej Purski return sii9234_clear_error(ctx); 387c0374eb8SMaciej Purski } 388c0374eb8SMaciej Purski 389c0374eb8SMaciej Purski static void force_usb_id_switch_open(struct sii9234 *ctx) 390c0374eb8SMaciej Purski { 391c0374eb8SMaciej Purski /* Disable CBUS discovery */ 392c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL1_REG, 0, 0x01); 393c0374eb8SMaciej Purski /* Force USB ID switch to open */ 394c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL6_REG, ~0, USB_ID_OVR); 395c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL3_REG, ~0, 0x86); 396c0374eb8SMaciej Purski /* Force upstream HPD to 0 when not in MHL mode. */ 397c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_INT_CTRL_REG, 0, 0x30); 398c0374eb8SMaciej Purski } 399c0374eb8SMaciej Purski 400c0374eb8SMaciej Purski static void release_usb_id_switch_open(struct sii9234 *ctx) 401c0374eb8SMaciej Purski { 402c0374eb8SMaciej Purski msleep(T_SRC_CBUS_FLOAT); 403c0374eb8SMaciej Purski /* Clear USB ID switch to open */ 404c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL6_REG, 0, USB_ID_OVR); 405c0374eb8SMaciej Purski /* Enable CBUS discovery */ 406c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL1_REG, ~0, 0x01); 407c0374eb8SMaciej Purski } 408c0374eb8SMaciej Purski 409c0374eb8SMaciej Purski static int sii9234_power_init(struct sii9234 *ctx) 410c0374eb8SMaciej Purski { 411c0374eb8SMaciej Purski /* Force the SiI9234 into the D0 state. */ 412c0374eb8SMaciej Purski tpi_writeb(ctx, TPI_DPD_REG, 0x3F); 413c0374eb8SMaciej Purski /* Enable TxPLL Clock */ 414c0374eb8SMaciej Purski hdmi_writeb(ctx, HDMI_RX_TMDS_CLK_EN_REG, 0x01); 415c0374eb8SMaciej Purski /* Enable Tx Clock Path & Equalizer */ 416c0374eb8SMaciej Purski hdmi_writeb(ctx, HDMI_RX_TMDS_CH_EN_REG, 0x15); 417c0374eb8SMaciej Purski /* Power Up TMDS */ 418c0374eb8SMaciej Purski mhl_tx_writeb(ctx, 0x08, 0x35); 419c0374eb8SMaciej Purski return sii9234_clear_error(ctx); 420c0374eb8SMaciej Purski } 421c0374eb8SMaciej Purski 422c0374eb8SMaciej Purski static int sii9234_hdmi_init(struct sii9234 *ctx) 423c0374eb8SMaciej Purski { 424c0374eb8SMaciej Purski hdmi_writeb(ctx, HDMI_RX_TMDS0_CCTRL1_REG, 0xC1); 425c0374eb8SMaciej Purski hdmi_writeb(ctx, HDMI_RX_PLL_CALREFSEL_REG, 0x03); 426c0374eb8SMaciej Purski hdmi_writeb(ctx, HDMI_RX_PLL_VCOCAL_REG, 0x20); 427c0374eb8SMaciej Purski hdmi_writeb(ctx, HDMI_RX_EQ_DATA0_REG, 0x8A); 428c0374eb8SMaciej Purski hdmi_writeb(ctx, HDMI_RX_EQ_DATA1_REG, 0x6A); 429c0374eb8SMaciej Purski hdmi_writeb(ctx, HDMI_RX_EQ_DATA2_REG, 0xAA); 430c0374eb8SMaciej Purski hdmi_writeb(ctx, HDMI_RX_EQ_DATA3_REG, 0xCA); 431c0374eb8SMaciej Purski hdmi_writeb(ctx, HDMI_RX_EQ_DATA4_REG, 0xEA); 432c0374eb8SMaciej Purski hdmi_writeb(ctx, HDMI_RX_TMDS_ZONE_CTRL_REG, 0xA0); 433c0374eb8SMaciej Purski hdmi_writeb(ctx, HDMI_RX_TMDS_MODE_CTRL_REG, 0x00); 434c0374eb8SMaciej Purski mhl_tx_writeb(ctx, MHL_TX_TMDS_CCTRL, 0x34); 435c0374eb8SMaciej Purski hdmi_writeb(ctx, 0x45, 0x44); 436c0374eb8SMaciej Purski hdmi_writeb(ctx, 0x31, 0x0A); 437c0374eb8SMaciej Purski hdmi_writeb(ctx, HDMI_RX_TMDS0_CCTRL1_REG, 0xC1); 438c0374eb8SMaciej Purski 439c0374eb8SMaciej Purski return sii9234_clear_error(ctx); 440c0374eb8SMaciej Purski } 441c0374eb8SMaciej Purski 442c0374eb8SMaciej Purski static int sii9234_mhl_tx_ctl_int(struct sii9234 *ctx) 443c0374eb8SMaciej Purski { 444c0374eb8SMaciej Purski mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL1_REG, 0xD0); 445c0374eb8SMaciej Purski mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL2_REG, 0xFC); 446c0374eb8SMaciej Purski mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL4_REG, 0xEB); 447c0374eb8SMaciej Purski mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL7_REG, 0x0C); 448c0374eb8SMaciej Purski 449c0374eb8SMaciej Purski return sii9234_clear_error(ctx); 450c0374eb8SMaciej Purski } 451c0374eb8SMaciej Purski 452c0374eb8SMaciej Purski static int sii9234_reset(struct sii9234 *ctx) 453c0374eb8SMaciej Purski { 454c0374eb8SMaciej Purski int ret; 455c0374eb8SMaciej Purski 456c0374eb8SMaciej Purski sii9234_clear_error(ctx); 457c0374eb8SMaciej Purski 458c0374eb8SMaciej Purski ret = sii9234_power_init(ctx); 459c0374eb8SMaciej Purski if (ret < 0) 460c0374eb8SMaciej Purski return ret; 461c0374eb8SMaciej Purski ret = sii9234_cbus_reset(ctx); 462c0374eb8SMaciej Purski if (ret < 0) 463c0374eb8SMaciej Purski return ret; 464c0374eb8SMaciej Purski ret = sii9234_hdmi_init(ctx); 465c0374eb8SMaciej Purski if (ret < 0) 466c0374eb8SMaciej Purski return ret; 467c0374eb8SMaciej Purski ret = sii9234_mhl_tx_ctl_int(ctx); 468c0374eb8SMaciej Purski if (ret < 0) 469c0374eb8SMaciej Purski return ret; 470c0374eb8SMaciej Purski 471c0374eb8SMaciej Purski /* Enable HDCP Compliance safety */ 472c0374eb8SMaciej Purski mhl_tx_writeb(ctx, 0x2B, 0x01); 473c0374eb8SMaciej Purski /* CBUS discovery cycle time for each drive and float = 150us */ 474c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL1_REG, 0x04, 0x06); 475c0374eb8SMaciej Purski /* Clear bit 6 (reg_skip_rgnd) */ 476c0374eb8SMaciej Purski mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL2_REG, (1 << 7) /* Reserved */ 477c0374eb8SMaciej Purski | 2 << ATT_THRESH_SHIFT | DEGLITCH_TIME_50MS); 478c0374eb8SMaciej Purski /* 479c0374eb8SMaciej Purski * Changed from 66 to 65 for 94[1:0] = 01 = 5k reg_cbusmhl_pup_sel 480c0374eb8SMaciej Purski * 1.8V CBUS VTH & GND threshold 481c0374eb8SMaciej Purski * to meet CTS 3.3.7.2 spec 482c0374eb8SMaciej Purski */ 483c0374eb8SMaciej Purski mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL5_REG, 0x77); 484c0374eb8SMaciej Purski cbus_writebm(ctx, CBUS_LINK_CONTROL_2_REG, ~0, MHL_INIT_TIMEOUT); 485c0374eb8SMaciej Purski mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL6_REG, 0xA0); 486c0374eb8SMaciej Purski /* RGND & single discovery attempt (RGND blocking) */ 487c0374eb8SMaciej Purski mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL6_REG, BLOCK_RGND_INT | 488c0374eb8SMaciej Purski DVRFLT_SEL | SINGLE_ATT); 489c0374eb8SMaciej Purski /* Use VBUS path of discovery state machine */ 490c0374eb8SMaciej Purski mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL8_REG, 0); 491c0374eb8SMaciej Purski /* 0x92[3] sets the CBUS / ID switch */ 492c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL6_REG, ~0, USB_ID_OVR); 493c0374eb8SMaciej Purski /* 494c0374eb8SMaciej Purski * To allow RGND engine to operate correctly. 495c0374eb8SMaciej Purski * When moving the chip from D2 to D0 (power up, init regs) 496c0374eb8SMaciej Purski * the values should be 497c0374eb8SMaciej Purski * 94[1:0] = 01 reg_cbusmhl_pup_sel[1:0] should be set for 5k 498c0374eb8SMaciej Purski * 93[7:6] = 10 reg_cbusdisc_pup_sel[1:0] should be 499c0374eb8SMaciej Purski * set for 10k (default) 500c0374eb8SMaciej Purski * 93[5:4] = 00 reg_cbusidle_pup_sel[1:0] = open (default) 501c0374eb8SMaciej Purski */ 502c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL3_REG, ~0, 0x86); 503c0374eb8SMaciej Purski /* 504c0374eb8SMaciej Purski * Change from CC to 8C to match 5K 505c0374eb8SMaciej Purski * to meet CTS 3.3.72 spec 506c0374eb8SMaciej Purski */ 507c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL4_REG, ~0, 0x8C); 508c0374eb8SMaciej Purski /* Configure the interrupt as active high */ 509c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_INT_CTRL_REG, 0, 0x06); 510c0374eb8SMaciej Purski 511c0374eb8SMaciej Purski msleep(25); 512c0374eb8SMaciej Purski 513c0374eb8SMaciej Purski /* Release usb_id switch */ 514c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL6_REG, 0, USB_ID_OVR); 515c0374eb8SMaciej Purski mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL1_REG, 0x27); 516c0374eb8SMaciej Purski 517c0374eb8SMaciej Purski ret = sii9234_clear_error(ctx); 518c0374eb8SMaciej Purski if (ret < 0) 519c0374eb8SMaciej Purski return ret; 520c0374eb8SMaciej Purski ret = sii9234_cbus_init(ctx); 521c0374eb8SMaciej Purski if (ret < 0) 522c0374eb8SMaciej Purski return ret; 523c0374eb8SMaciej Purski 524c0374eb8SMaciej Purski /* Enable Auto soft reset on SCDT = 0 */ 525c0374eb8SMaciej Purski mhl_tx_writeb(ctx, 0x05, 0x04); 526c0374eb8SMaciej Purski /* HDMI Transcode mode enable */ 527c0374eb8SMaciej Purski mhl_tx_writeb(ctx, 0x0D, 0x1C); 528c0374eb8SMaciej Purski mhl_tx_writeb(ctx, MHL_TX_INTR4_ENABLE_REG, 529c0374eb8SMaciej Purski RGND_READY_MASK | CBUS_LKOUT_MASK 530c0374eb8SMaciej Purski | MHL_DISC_FAIL_MASK | MHL_EST_MASK); 531c0374eb8SMaciej Purski mhl_tx_writeb(ctx, MHL_TX_INTR1_ENABLE_REG, 0x60); 532c0374eb8SMaciej Purski 533c0374eb8SMaciej Purski /* This point is very important before measure RGND impedance */ 534c0374eb8SMaciej Purski force_usb_id_switch_open(ctx); 535c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL4_REG, 0, 0xF0); 536c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL5_REG, 0, 0x03); 537c0374eb8SMaciej Purski release_usb_id_switch_open(ctx); 538c0374eb8SMaciej Purski 539c0374eb8SMaciej Purski /* Force upstream HPD to 0 when not in MHL mode */ 540c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_INT_CTRL_REG, 0, 1 << 5); 541c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_INT_CTRL_REG, ~0, 1 << 4); 542c0374eb8SMaciej Purski 543c0374eb8SMaciej Purski return sii9234_clear_error(ctx); 544c0374eb8SMaciej Purski } 545c0374eb8SMaciej Purski 546c0374eb8SMaciej Purski static int sii9234_goto_d3(struct sii9234 *ctx) 547c0374eb8SMaciej Purski { 548c0374eb8SMaciej Purski int ret; 549c0374eb8SMaciej Purski 550c0374eb8SMaciej Purski dev_dbg(ctx->dev, "sii9234: detection started d3\n"); 551c0374eb8SMaciej Purski 552c0374eb8SMaciej Purski ret = sii9234_reset(ctx); 553c0374eb8SMaciej Purski if (ret < 0) 554c0374eb8SMaciej Purski goto exit; 555c0374eb8SMaciej Purski 556c0374eb8SMaciej Purski hdmi_writeb(ctx, 0x01, 0x03); 557c0374eb8SMaciej Purski tpi_writebm(ctx, TPI_DPD_REG, 0, 1); 558c0374eb8SMaciej Purski /* I2C above is expected to fail because power goes down */ 559c0374eb8SMaciej Purski sii9234_clear_error(ctx); 560c0374eb8SMaciej Purski 561c0374eb8SMaciej Purski ctx->state = ST_D3; 562c0374eb8SMaciej Purski 563c0374eb8SMaciej Purski return 0; 564c0374eb8SMaciej Purski exit: 565c0374eb8SMaciej Purski dev_err(ctx->dev, "%s failed\n", __func__); 566c0374eb8SMaciej Purski return -1; 567c0374eb8SMaciej Purski } 568c0374eb8SMaciej Purski 569c0374eb8SMaciej Purski static int sii9234_hw_on(struct sii9234 *ctx) 570c0374eb8SMaciej Purski { 571c0374eb8SMaciej Purski return regulator_bulk_enable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 572c0374eb8SMaciej Purski } 573c0374eb8SMaciej Purski 574c0374eb8SMaciej Purski static void sii9234_hw_off(struct sii9234 *ctx) 575c0374eb8SMaciej Purski { 576c0374eb8SMaciej Purski gpiod_set_value(ctx->gpio_reset, 1); 577c0374eb8SMaciej Purski msleep(20); 578c0374eb8SMaciej Purski regulator_bulk_disable(ARRAY_SIZE(ctx->supplies), ctx->supplies); 579c0374eb8SMaciej Purski } 580c0374eb8SMaciej Purski 581c0374eb8SMaciej Purski static void sii9234_hw_reset(struct sii9234 *ctx) 582c0374eb8SMaciej Purski { 583c0374eb8SMaciej Purski gpiod_set_value(ctx->gpio_reset, 1); 584c0374eb8SMaciej Purski msleep(20); 585c0374eb8SMaciej Purski gpiod_set_value(ctx->gpio_reset, 0); 586c0374eb8SMaciej Purski } 587c0374eb8SMaciej Purski 588c0374eb8SMaciej Purski static void sii9234_cable_in(struct sii9234 *ctx) 589c0374eb8SMaciej Purski { 590c0374eb8SMaciej Purski int ret; 591c0374eb8SMaciej Purski 592c0374eb8SMaciej Purski mutex_lock(&ctx->lock); 593c0374eb8SMaciej Purski if (ctx->state != ST_OFF) 594c0374eb8SMaciej Purski goto unlock; 595c0374eb8SMaciej Purski ret = sii9234_hw_on(ctx); 596c0374eb8SMaciej Purski if (ret < 0) 597c0374eb8SMaciej Purski goto unlock; 598c0374eb8SMaciej Purski 599c0374eb8SMaciej Purski sii9234_hw_reset(ctx); 600c0374eb8SMaciej Purski sii9234_goto_d3(ctx); 601c0374eb8SMaciej Purski /* To avoid irq storm, when hw is in meta state */ 602c0374eb8SMaciej Purski enable_irq(to_i2c_client(ctx->dev)->irq); 603c0374eb8SMaciej Purski 604c0374eb8SMaciej Purski unlock: 605c0374eb8SMaciej Purski mutex_unlock(&ctx->lock); 606c0374eb8SMaciej Purski } 607c0374eb8SMaciej Purski 608c0374eb8SMaciej Purski static void sii9234_cable_out(struct sii9234 *ctx) 609c0374eb8SMaciej Purski { 610c0374eb8SMaciej Purski mutex_lock(&ctx->lock); 611c0374eb8SMaciej Purski 612c0374eb8SMaciej Purski if (ctx->state == ST_OFF) 613c0374eb8SMaciej Purski goto unlock; 614c0374eb8SMaciej Purski 615c0374eb8SMaciej Purski disable_irq(to_i2c_client(ctx->dev)->irq); 616c0374eb8SMaciej Purski tpi_writeb(ctx, TPI_DPD_REG, 0); 617c0374eb8SMaciej Purski /* Turn on&off hpd festure for only QCT HDMI */ 618c0374eb8SMaciej Purski sii9234_hw_off(ctx); 619c0374eb8SMaciej Purski 620c0374eb8SMaciej Purski ctx->state = ST_OFF; 621c0374eb8SMaciej Purski 622c0374eb8SMaciej Purski unlock: 623c0374eb8SMaciej Purski mutex_unlock(&ctx->lock); 624c0374eb8SMaciej Purski } 625c0374eb8SMaciej Purski 626c0374eb8SMaciej Purski static enum sii9234_state sii9234_rgnd_ready_irq(struct sii9234 *ctx) 627c0374eb8SMaciej Purski { 628c0374eb8SMaciej Purski int value; 629c0374eb8SMaciej Purski 630c0374eb8SMaciej Purski if (ctx->state == ST_D3) { 631c0374eb8SMaciej Purski int ret; 632c0374eb8SMaciej Purski 633c0374eb8SMaciej Purski dev_dbg(ctx->dev, "RGND_READY_INT\n"); 634c0374eb8SMaciej Purski sii9234_hw_reset(ctx); 635c0374eb8SMaciej Purski 636c0374eb8SMaciej Purski ret = sii9234_reset(ctx); 637c0374eb8SMaciej Purski if (ret < 0) { 638c0374eb8SMaciej Purski dev_err(ctx->dev, "sii9234_reset() failed\n"); 639c0374eb8SMaciej Purski return ST_FAILURE; 640c0374eb8SMaciej Purski } 641c0374eb8SMaciej Purski 642c0374eb8SMaciej Purski return ST_RGND_INIT; 643c0374eb8SMaciej Purski } 644c0374eb8SMaciej Purski 645c0374eb8SMaciej Purski /* Got interrupt in inappropriate state */ 646c0374eb8SMaciej Purski if (ctx->state != ST_RGND_INIT) 647c0374eb8SMaciej Purski return ST_FAILURE; 648c0374eb8SMaciej Purski 649c0374eb8SMaciej Purski value = mhl_tx_readb(ctx, MHL_TX_STAT2_REG); 650c0374eb8SMaciej Purski if (sii9234_clear_error(ctx)) 651c0374eb8SMaciej Purski return ST_FAILURE; 652c0374eb8SMaciej Purski 653c0374eb8SMaciej Purski if ((value & RGND_INTP_MASK) != RGND_INTP_1K) { 654c0374eb8SMaciej Purski dev_warn(ctx->dev, "RGND is not 1k\n"); 655c0374eb8SMaciej Purski return ST_RGND_INIT; 656c0374eb8SMaciej Purski } 657c0374eb8SMaciej Purski dev_dbg(ctx->dev, "RGND 1K!!\n"); 658c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL4_REG, ~0, 0x8C); 659c0374eb8SMaciej Purski mhl_tx_writeb(ctx, MHL_TX_DISC_CTRL5_REG, 0x77); 660c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL6_REG, ~0, 0x05); 661c0374eb8SMaciej Purski if (sii9234_clear_error(ctx)) 662c0374eb8SMaciej Purski return ST_FAILURE; 663c0374eb8SMaciej Purski 664c0374eb8SMaciej Purski msleep(T_SRC_VBUS_CBUS_TO_STABLE); 665c0374eb8SMaciej Purski return ST_RGND_1K; 666c0374eb8SMaciej Purski } 667c0374eb8SMaciej Purski 668c0374eb8SMaciej Purski static enum sii9234_state sii9234_mhl_established(struct sii9234 *ctx) 669c0374eb8SMaciej Purski { 670c0374eb8SMaciej Purski dev_dbg(ctx->dev, "mhl est interrupt\n"); 671c0374eb8SMaciej Purski 672c0374eb8SMaciej Purski /* Discovery override */ 673c0374eb8SMaciej Purski mhl_tx_writeb(ctx, MHL_TX_MHLTX_CTL1_REG, 0x10); 674c0374eb8SMaciej Purski /* Increase DDC translation layer timer (byte mode) */ 675c0374eb8SMaciej Purski cbus_writeb(ctx, 0x07, 0x32); 676c0374eb8SMaciej Purski cbus_writebm(ctx, 0x44, ~0, 1 << 1); 677c0374eb8SMaciej Purski /* Keep the discovery enabled. Need RGND interrupt */ 678c0374eb8SMaciej Purski mhl_tx_writebm(ctx, MHL_TX_DISC_CTRL1_REG, ~0, 1); 679c0374eb8SMaciej Purski mhl_tx_writeb(ctx, MHL_TX_INTR1_ENABLE_REG, 680c0374eb8SMaciej Purski RSEN_CHANGE_INT_MASK | HPD_CHANGE_INT_MASK); 681c0374eb8SMaciej Purski 682c0374eb8SMaciej Purski if (sii9234_clear_error(ctx)) 683c0374eb8SMaciej Purski return ST_FAILURE; 684c0374eb8SMaciej Purski 685c0374eb8SMaciej Purski return ST_MHL_ESTABLISHED; 686c0374eb8SMaciej Purski } 687c0374eb8SMaciej Purski 688c0374eb8SMaciej Purski static enum sii9234_state sii9234_hpd_change(struct sii9234 *ctx) 689c0374eb8SMaciej Purski { 690c0374eb8SMaciej Purski int value; 691c0374eb8SMaciej Purski 692c0374eb8SMaciej Purski value = cbus_readb(ctx, CBUS_MSC_REQ_ABORT_REASON_REG); 693c0374eb8SMaciej Purski if (sii9234_clear_error(ctx)) 694c0374eb8SMaciej Purski return ST_FAILURE; 695c0374eb8SMaciej Purski 696c0374eb8SMaciej Purski if (value & SET_HPD_DOWNSTREAM) { 697c0374eb8SMaciej Purski /* Downstream HPD High, Enable TMDS */ 698c0374eb8SMaciej Purski sii9234_tmds_control(ctx, true); 699c0374eb8SMaciej Purski } else { 700c0374eb8SMaciej Purski /* Downstream HPD Low, Disable TMDS */ 701c0374eb8SMaciej Purski sii9234_tmds_control(ctx, false); 702c0374eb8SMaciej Purski } 703c0374eb8SMaciej Purski 704c0374eb8SMaciej Purski return ctx->state; 705c0374eb8SMaciej Purski } 706c0374eb8SMaciej Purski 707c0374eb8SMaciej Purski static enum sii9234_state sii9234_rsen_change(struct sii9234 *ctx) 708c0374eb8SMaciej Purski { 709c0374eb8SMaciej Purski int value; 710c0374eb8SMaciej Purski 711c0374eb8SMaciej Purski /* Work_around code to handle wrong interrupt */ 712c0374eb8SMaciej Purski if (ctx->state != ST_RGND_1K) { 713c0374eb8SMaciej Purski dev_err(ctx->dev, "RSEN_HIGH without RGND_1K\n"); 714c0374eb8SMaciej Purski return ST_FAILURE; 715c0374eb8SMaciej Purski } 716c0374eb8SMaciej Purski value = mhl_tx_readb(ctx, MHL_TX_SYSSTAT_REG); 717c0374eb8SMaciej Purski if (value < 0) 718c0374eb8SMaciej Purski return ST_FAILURE; 719c0374eb8SMaciej Purski 720c0374eb8SMaciej Purski if (value & RSEN_STATUS) { 721c0374eb8SMaciej Purski dev_dbg(ctx->dev, "MHL cable connected.. RSEN High\n"); 722c0374eb8SMaciej Purski return ST_RSEN_HIGH; 723c0374eb8SMaciej Purski } 724c0374eb8SMaciej Purski dev_dbg(ctx->dev, "RSEN lost\n"); 725c0374eb8SMaciej Purski /* 726c0374eb8SMaciej Purski * Once RSEN loss is confirmed,we need to check 727c0374eb8SMaciej Purski * based on cable status and chip power status,whether 728c0374eb8SMaciej Purski * it is SINK Loss(HDMI cable not connected, TV Off) 729c0374eb8SMaciej Purski * or MHL cable disconnection 730c0374eb8SMaciej Purski * TODO: Define the below mhl_disconnection() 731c0374eb8SMaciej Purski */ 732c0374eb8SMaciej Purski msleep(T_SRC_RXSENSE_DEGLITCH); 733c0374eb8SMaciej Purski value = mhl_tx_readb(ctx, MHL_TX_SYSSTAT_REG); 734c0374eb8SMaciej Purski if (value < 0) 735c0374eb8SMaciej Purski return ST_FAILURE; 736c0374eb8SMaciej Purski dev_dbg(ctx->dev, "sys_stat: %x\n", value); 737c0374eb8SMaciej Purski 738c0374eb8SMaciej Purski if (value & RSEN_STATUS) { 739c0374eb8SMaciej Purski dev_dbg(ctx->dev, "RSEN recovery\n"); 740c0374eb8SMaciej Purski return ST_RSEN_HIGH; 741c0374eb8SMaciej Purski } 742c0374eb8SMaciej Purski dev_dbg(ctx->dev, "RSEN Really LOW\n"); 743c0374eb8SMaciej Purski /* To meet CTS 3.3.22.2 spec */ 744c0374eb8SMaciej Purski sii9234_tmds_control(ctx, false); 745c0374eb8SMaciej Purski force_usb_id_switch_open(ctx); 746c0374eb8SMaciej Purski release_usb_id_switch_open(ctx); 747c0374eb8SMaciej Purski 748c0374eb8SMaciej Purski return ST_FAILURE; 749c0374eb8SMaciej Purski } 750c0374eb8SMaciej Purski 751c0374eb8SMaciej Purski static irqreturn_t sii9234_irq_thread(int irq, void *data) 752c0374eb8SMaciej Purski { 753c0374eb8SMaciej Purski struct sii9234 *ctx = data; 754c0374eb8SMaciej Purski int intr1, intr4; 755c0374eb8SMaciej Purski int intr1_en, intr4_en; 756c0374eb8SMaciej Purski int cbus_intr1, cbus_intr2; 757c0374eb8SMaciej Purski 758c0374eb8SMaciej Purski dev_dbg(ctx->dev, "%s\n", __func__); 759c0374eb8SMaciej Purski 760c0374eb8SMaciej Purski mutex_lock(&ctx->lock); 761c0374eb8SMaciej Purski 762c0374eb8SMaciej Purski intr1 = mhl_tx_readb(ctx, MHL_TX_INTR1_REG); 763c0374eb8SMaciej Purski intr4 = mhl_tx_readb(ctx, MHL_TX_INTR4_REG); 764c0374eb8SMaciej Purski intr1_en = mhl_tx_readb(ctx, MHL_TX_INTR1_ENABLE_REG); 765c0374eb8SMaciej Purski intr4_en = mhl_tx_readb(ctx, MHL_TX_INTR4_ENABLE_REG); 766c0374eb8SMaciej Purski cbus_intr1 = cbus_readb(ctx, CBUS_INT_STATUS_1_REG); 767c0374eb8SMaciej Purski cbus_intr2 = cbus_readb(ctx, CBUS_INT_STATUS_2_REG); 768c0374eb8SMaciej Purski 769c0374eb8SMaciej Purski if (sii9234_clear_error(ctx)) 770c0374eb8SMaciej Purski goto done; 771c0374eb8SMaciej Purski 772c0374eb8SMaciej Purski dev_dbg(ctx->dev, "irq %02x/%02x %02x/%02x %02x/%02x\n", 773c0374eb8SMaciej Purski intr1, intr1_en, intr4, intr4_en, cbus_intr1, cbus_intr2); 774c0374eb8SMaciej Purski 775c0374eb8SMaciej Purski if (intr4 & RGND_READY_INT) 776c0374eb8SMaciej Purski ctx->state = sii9234_rgnd_ready_irq(ctx); 777c0374eb8SMaciej Purski if (intr1 & RSEN_CHANGE_INT) 778c0374eb8SMaciej Purski ctx->state = sii9234_rsen_change(ctx); 779c0374eb8SMaciej Purski if (intr4 & MHL_EST_INT) 780c0374eb8SMaciej Purski ctx->state = sii9234_mhl_established(ctx); 781c0374eb8SMaciej Purski if (intr1 & HPD_CHANGE_INT) 782c0374eb8SMaciej Purski ctx->state = sii9234_hpd_change(ctx); 783c0374eb8SMaciej Purski if (intr4 & CBUS_LKOUT_INT) 784c0374eb8SMaciej Purski ctx->state = ST_FAILURE; 785c0374eb8SMaciej Purski if (intr4 & MHL_DISC_FAIL_INT) 786c0374eb8SMaciej Purski ctx->state = ST_FAILURE_DISCOVERY; 787c0374eb8SMaciej Purski 788c0374eb8SMaciej Purski done: 789c0374eb8SMaciej Purski /* Clean interrupt status and pending flags */ 790c0374eb8SMaciej Purski mhl_tx_writeb(ctx, MHL_TX_INTR1_REG, intr1); 791c0374eb8SMaciej Purski mhl_tx_writeb(ctx, MHL_TX_INTR4_REG, intr4); 792c0374eb8SMaciej Purski cbus_writeb(ctx, CBUS_MHL_STATUS_REG_0, 0xFF); 793c0374eb8SMaciej Purski cbus_writeb(ctx, CBUS_MHL_STATUS_REG_1, 0xFF); 794c0374eb8SMaciej Purski cbus_writeb(ctx, CBUS_INT_STATUS_1_REG, cbus_intr1); 795c0374eb8SMaciej Purski cbus_writeb(ctx, CBUS_INT_STATUS_2_REG, cbus_intr2); 796c0374eb8SMaciej Purski 797c0374eb8SMaciej Purski sii9234_clear_error(ctx); 798c0374eb8SMaciej Purski 799c0374eb8SMaciej Purski if (ctx->state == ST_FAILURE) { 800c0374eb8SMaciej Purski dev_dbg(ctx->dev, "try to reset after failure\n"); 801c0374eb8SMaciej Purski sii9234_hw_reset(ctx); 802c0374eb8SMaciej Purski sii9234_goto_d3(ctx); 803c0374eb8SMaciej Purski } 804c0374eb8SMaciej Purski 805c0374eb8SMaciej Purski if (ctx->state == ST_FAILURE_DISCOVERY) { 806c0374eb8SMaciej Purski dev_err(ctx->dev, "discovery failed, no power for MHL?\n"); 807c0374eb8SMaciej Purski tpi_writebm(ctx, TPI_DPD_REG, 0, 1); 808c0374eb8SMaciej Purski ctx->state = ST_D3; 809c0374eb8SMaciej Purski } 810c0374eb8SMaciej Purski 811c0374eb8SMaciej Purski mutex_unlock(&ctx->lock); 812c0374eb8SMaciej Purski 813c0374eb8SMaciej Purski return IRQ_HANDLED; 814c0374eb8SMaciej Purski } 815c0374eb8SMaciej Purski 816c0374eb8SMaciej Purski static int sii9234_init_resources(struct sii9234 *ctx, 817c0374eb8SMaciej Purski struct i2c_client *client) 818c0374eb8SMaciej Purski { 819c412187dSWolfram Sang struct i2c_adapter *adapter = client->adapter; 820c0374eb8SMaciej Purski int ret; 821c0374eb8SMaciej Purski 822c0374eb8SMaciej Purski if (!ctx->dev->of_node) { 823c0374eb8SMaciej Purski dev_err(ctx->dev, "not DT device\n"); 824c0374eb8SMaciej Purski return -ENODEV; 825c0374eb8SMaciej Purski } 826c0374eb8SMaciej Purski 827c0374eb8SMaciej Purski ctx->gpio_reset = devm_gpiod_get(ctx->dev, "reset", GPIOD_OUT_LOW); 828c0374eb8SMaciej Purski if (IS_ERR(ctx->gpio_reset)) { 829c0374eb8SMaciej Purski dev_err(ctx->dev, "failed to get reset gpio from DT\n"); 830c0374eb8SMaciej Purski return PTR_ERR(ctx->gpio_reset); 831c0374eb8SMaciej Purski } 832c0374eb8SMaciej Purski 833c0374eb8SMaciej Purski ctx->supplies[0].supply = "avcc12"; 834c0374eb8SMaciej Purski ctx->supplies[1].supply = "avcc33"; 835c0374eb8SMaciej Purski ctx->supplies[2].supply = "iovcc18"; 836c0374eb8SMaciej Purski ctx->supplies[3].supply = "cvcc12"; 837c0374eb8SMaciej Purski ret = devm_regulator_bulk_get(ctx->dev, 4, ctx->supplies); 838c0374eb8SMaciej Purski if (ret) { 839c0374eb8SMaciej Purski dev_err(ctx->dev, "regulator_bulk failed\n"); 840c0374eb8SMaciej Purski return ret; 841c0374eb8SMaciej Purski } 842c0374eb8SMaciej Purski 843c0374eb8SMaciej Purski ctx->client[I2C_MHL] = client; 844c0374eb8SMaciej Purski 845c0374eb8SMaciej Purski ctx->client[I2C_TPI] = i2c_new_dummy(adapter, I2C_TPI_ADDR); 846c0374eb8SMaciej Purski if (!ctx->client[I2C_TPI]) { 847c0374eb8SMaciej Purski dev_err(ctx->dev, "failed to create TPI client\n"); 848c0374eb8SMaciej Purski return -ENODEV; 849c0374eb8SMaciej Purski } 850c0374eb8SMaciej Purski 851c0374eb8SMaciej Purski ctx->client[I2C_HDMI] = i2c_new_dummy(adapter, I2C_HDMI_ADDR); 852c0374eb8SMaciej Purski if (!ctx->client[I2C_HDMI]) { 853c0374eb8SMaciej Purski dev_err(ctx->dev, "failed to create HDMI RX client\n"); 854c0374eb8SMaciej Purski goto fail_tpi; 855c0374eb8SMaciej Purski } 856c0374eb8SMaciej Purski 857c0374eb8SMaciej Purski ctx->client[I2C_CBUS] = i2c_new_dummy(adapter, I2C_CBUS_ADDR); 858c0374eb8SMaciej Purski if (!ctx->client[I2C_CBUS]) { 859c0374eb8SMaciej Purski dev_err(ctx->dev, "failed to create CBUS client\n"); 860c0374eb8SMaciej Purski goto fail_hdmi; 861c0374eb8SMaciej Purski } 862c0374eb8SMaciej Purski 863c0374eb8SMaciej Purski return 0; 864c0374eb8SMaciej Purski 865c0374eb8SMaciej Purski fail_hdmi: 866c0374eb8SMaciej Purski i2c_unregister_device(ctx->client[I2C_HDMI]); 867c0374eb8SMaciej Purski fail_tpi: 868c0374eb8SMaciej Purski i2c_unregister_device(ctx->client[I2C_TPI]); 869c0374eb8SMaciej Purski 870c0374eb8SMaciej Purski return -ENODEV; 871c0374eb8SMaciej Purski } 872c0374eb8SMaciej Purski 873c0374eb8SMaciej Purski static void sii9234_deinit_resources(struct sii9234 *ctx) 874c0374eb8SMaciej Purski { 875c0374eb8SMaciej Purski i2c_unregister_device(ctx->client[I2C_CBUS]); 876c0374eb8SMaciej Purski i2c_unregister_device(ctx->client[I2C_HDMI]); 877c0374eb8SMaciej Purski i2c_unregister_device(ctx->client[I2C_TPI]); 878c0374eb8SMaciej Purski } 879c0374eb8SMaciej Purski 880c0374eb8SMaciej Purski static inline struct sii9234 *bridge_to_sii9234(struct drm_bridge *bridge) 881c0374eb8SMaciej Purski { 882c0374eb8SMaciej Purski return container_of(bridge, struct sii9234, bridge); 883c0374eb8SMaciej Purski } 884c0374eb8SMaciej Purski 885c0374eb8SMaciej Purski static enum drm_mode_status sii9234_mode_valid(struct drm_bridge *bridge, 886c0374eb8SMaciej Purski const struct drm_display_mode *mode) 887c0374eb8SMaciej Purski { 888c0374eb8SMaciej Purski if (mode->clock > MHL1_MAX_CLK) 889c0374eb8SMaciej Purski return MODE_CLOCK_HIGH; 890c0374eb8SMaciej Purski 891c0374eb8SMaciej Purski return MODE_OK; 892c0374eb8SMaciej Purski } 893c0374eb8SMaciej Purski 894c0374eb8SMaciej Purski static const struct drm_bridge_funcs sii9234_bridge_funcs = { 895c0374eb8SMaciej Purski .mode_valid = sii9234_mode_valid, 896c0374eb8SMaciej Purski }; 897c0374eb8SMaciej Purski 898c0374eb8SMaciej Purski static int sii9234_probe(struct i2c_client *client, 899c0374eb8SMaciej Purski const struct i2c_device_id *id) 900c0374eb8SMaciej Purski { 901c412187dSWolfram Sang struct i2c_adapter *adapter = client->adapter; 902c0374eb8SMaciej Purski struct sii9234 *ctx; 903c0374eb8SMaciej Purski struct device *dev = &client->dev; 904c0374eb8SMaciej Purski int ret; 905c0374eb8SMaciej Purski 906c0374eb8SMaciej Purski ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL); 907c0374eb8SMaciej Purski if (!ctx) 908c0374eb8SMaciej Purski return -ENOMEM; 909c0374eb8SMaciej Purski 910c0374eb8SMaciej Purski ctx->dev = dev; 911c0374eb8SMaciej Purski mutex_init(&ctx->lock); 912c0374eb8SMaciej Purski 913c0374eb8SMaciej Purski if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { 914c0374eb8SMaciej Purski dev_err(dev, "I2C adapter lacks SMBUS feature\n"); 915c0374eb8SMaciej Purski return -EIO; 916c0374eb8SMaciej Purski } 917c0374eb8SMaciej Purski 918c0374eb8SMaciej Purski if (!client->irq) { 919c0374eb8SMaciej Purski dev_err(dev, "no irq provided\n"); 920c0374eb8SMaciej Purski return -EINVAL; 921c0374eb8SMaciej Purski } 922c0374eb8SMaciej Purski 923c0374eb8SMaciej Purski irq_set_status_flags(client->irq, IRQ_NOAUTOEN); 924c0374eb8SMaciej Purski ret = devm_request_threaded_irq(dev, client->irq, NULL, 925c0374eb8SMaciej Purski sii9234_irq_thread, 926c0374eb8SMaciej Purski IRQF_TRIGGER_HIGH | IRQF_ONESHOT, 927c0374eb8SMaciej Purski "sii9234", ctx); 928c0374eb8SMaciej Purski if (ret < 0) { 929c0374eb8SMaciej Purski dev_err(dev, "failed to install IRQ handler\n"); 930c0374eb8SMaciej Purski return ret; 931c0374eb8SMaciej Purski } 932c0374eb8SMaciej Purski 933c0374eb8SMaciej Purski ret = sii9234_init_resources(ctx, client); 934c0374eb8SMaciej Purski if (ret < 0) 935c0374eb8SMaciej Purski return ret; 936c0374eb8SMaciej Purski 937c0374eb8SMaciej Purski i2c_set_clientdata(client, ctx); 938c0374eb8SMaciej Purski 939c0374eb8SMaciej Purski ctx->bridge.funcs = &sii9234_bridge_funcs; 940c0374eb8SMaciej Purski ctx->bridge.of_node = dev->of_node; 941c0374eb8SMaciej Purski drm_bridge_add(&ctx->bridge); 942c0374eb8SMaciej Purski 943c0374eb8SMaciej Purski sii9234_cable_in(ctx); 944c0374eb8SMaciej Purski 945c0374eb8SMaciej Purski return 0; 946c0374eb8SMaciej Purski } 947c0374eb8SMaciej Purski 948c0374eb8SMaciej Purski static int sii9234_remove(struct i2c_client *client) 949c0374eb8SMaciej Purski { 950c0374eb8SMaciej Purski struct sii9234 *ctx = i2c_get_clientdata(client); 951c0374eb8SMaciej Purski 952c0374eb8SMaciej Purski sii9234_cable_out(ctx); 953c0374eb8SMaciej Purski drm_bridge_remove(&ctx->bridge); 954c0374eb8SMaciej Purski sii9234_deinit_resources(ctx); 955c0374eb8SMaciej Purski 956c0374eb8SMaciej Purski return 0; 957c0374eb8SMaciej Purski } 958c0374eb8SMaciej Purski 959c0374eb8SMaciej Purski static const struct of_device_id sii9234_dt_match[] = { 960c0374eb8SMaciej Purski { .compatible = "sil,sii9234" }, 961c0374eb8SMaciej Purski { }, 962c0374eb8SMaciej Purski }; 963c0374eb8SMaciej Purski MODULE_DEVICE_TABLE(of, sii9234_dt_match); 964c0374eb8SMaciej Purski 965c0374eb8SMaciej Purski static const struct i2c_device_id sii9234_id[] = { 966c0374eb8SMaciej Purski { "SII9234", 0 }, 967c0374eb8SMaciej Purski { }, 968c0374eb8SMaciej Purski }; 969c0374eb8SMaciej Purski MODULE_DEVICE_TABLE(i2c, sii9234_id); 970c0374eb8SMaciej Purski 971c0374eb8SMaciej Purski static struct i2c_driver sii9234_driver = { 972c0374eb8SMaciej Purski .driver = { 973c0374eb8SMaciej Purski .name = "sii9234", 974c0374eb8SMaciej Purski .of_match_table = sii9234_dt_match, 975c0374eb8SMaciej Purski }, 976c0374eb8SMaciej Purski .probe = sii9234_probe, 977c0374eb8SMaciej Purski .remove = sii9234_remove, 978c0374eb8SMaciej Purski .id_table = sii9234_id, 979c0374eb8SMaciej Purski }; 980c0374eb8SMaciej Purski 981c0374eb8SMaciej Purski module_i2c_driver(sii9234_driver); 982c0374eb8SMaciej Purski MODULE_LICENSE("GPL"); 983