diff options
Diffstat (limited to 'drivers/gpu/drm/i2c')
-rw-r--r-- | drivers/gpu/drm/i2c/Kconfig | 1 | ||||
-rw-r--r-- | drivers/gpu/drm/i2c/sil164_drv.c | 3 | ||||
-rw-r--r-- | drivers/gpu/drm/i2c/tda9950.c | 5 | ||||
-rw-r--r-- | drivers/gpu/drm/i2c/tda998x_drv.c | 555 |
4 files changed, 379 insertions, 185 deletions
diff --git a/drivers/gpu/drm/i2c/Kconfig b/drivers/gpu/drm/i2c/Kconfig index 3a232f5ff0a1..65d3acb61c03 100644 --- a/drivers/gpu/drm/i2c/Kconfig +++ b/drivers/gpu/drm/i2c/Kconfig @@ -22,6 +22,7 @@ config DRM_I2C_SIL164 config DRM_I2C_NXP_TDA998X tristate "NXP Semiconductors TDA998X HDMI encoder" default m if DRM_TILCDC + select CEC_CORE if CEC_NOTIFIER select SND_SOC_HDMI_CODEC if SND_SOC help Support for NXP Semiconductors TDA998X HDMI encoders. diff --git a/drivers/gpu/drm/i2c/sil164_drv.c b/drivers/gpu/drm/i2c/sil164_drv.c index ecaa58757529..c52d7a3af786 100644 --- a/drivers/gpu/drm/i2c/sil164_drv.c +++ b/drivers/gpu/drm/i2c/sil164_drv.c @@ -326,8 +326,7 @@ sil164_encoder_destroy(struct drm_encoder *encoder) { struct sil164_priv *priv = to_sil164_priv(encoder); - if (priv->duallink_slave) - i2c_unregister_device(priv->duallink_slave); + i2c_unregister_device(priv->duallink_slave); kfree(priv); drm_i2c_encoder_destroy(encoder); diff --git a/drivers/gpu/drm/i2c/tda9950.c b/drivers/gpu/drm/i2c/tda9950.c index ccd355d0c123..250b5e02a314 100644 --- a/drivers/gpu/drm/i2c/tda9950.c +++ b/drivers/gpu/drm/i2c/tda9950.c @@ -76,9 +76,12 @@ struct tda9950_priv { static int tda9950_write_range(struct i2c_client *client, u8 addr, u8 *p, int cnt) { struct i2c_msg msg; - u8 buf[cnt + 1]; + u8 buf[CEC_MAX_MSG_SIZE + 3]; int ret; + if (WARN_ON(cnt > sizeof(buf) - 1)) + return -EINVAL; + buf[0] = addr; memcpy(buf + 1, p, cnt); diff --git a/drivers/gpu/drm/i2c/tda998x_drv.c b/drivers/gpu/drm/i2c/tda998x_drv.c index 16e0439cad44..a7c39f39793f 100644 --- a/drivers/gpu/drm/i2c/tda998x_drv.c +++ b/drivers/gpu/drm/i2c/tda998x_drv.c @@ -16,8 +16,10 @@ */ #include <linux/component.h> +#include <linux/gpio/consumer.h> #include <linux/hdmi.h> #include <linux/module.h> +#include <linux/platform_data/tda9950.h> #include <linux/irq.h> #include <sound/asoundef.h> #include <sound/hdmi-codec.h> @@ -29,6 +31,8 @@ #include <drm/drm_of.h> #include <drm/i2c/tda998x.h> +#include <media/cec-notifier.h> + #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__) struct tda998x_audio_port { @@ -55,6 +59,7 @@ struct tda998x_priv { struct platform_device *audio_pdev; struct mutex audio_mutex; + struct mutex edid_mutex; wait_queue_head_t wq_edid; volatile int wq_edid_wait; @@ -64,16 +69,21 @@ struct tda998x_priv { bool edid_delay_active; struct drm_encoder encoder; + struct drm_bridge bridge; struct drm_connector connector; struct tda998x_audio_port audio_port[2]; + struct tda9950_glue cec_glue; + struct gpio_desc *calib; + struct cec_notifier *cec_notify; }; #define conn_to_tda998x_priv(x) \ container_of(x, struct tda998x_priv, connector) - #define enc_to_tda998x_priv(x) \ container_of(x, struct tda998x_priv, encoder) +#define bridge_to_tda998x_priv(x) \ + container_of(x, struct tda998x_priv, bridge) /* The TDA9988 series of devices use a paged register scheme.. to simplify * things we encode the page # in upper bits of the register #. To read/ @@ -345,6 +355,12 @@ struct tda998x_priv { #define REG_CEC_INTSTATUS 0xee /* read */ # define CEC_INTSTATUS_CEC (1 << 0) # define CEC_INTSTATUS_HDMI (1 << 1) +#define REG_CEC_CAL_XOSC_CTRL1 0xf2 +# define CEC_CAL_XOSC_CTRL1_ENA_CAL BIT(0) +#define REG_CEC_DES_FREQ2 0xf5 +# define CEC_DES_FREQ2_DIS_AUTOCAL BIT(7) +#define REG_CEC_CLK 0xf6 +# define CEC_CLK_FRO 0x11 #define REG_CEC_FRO_IM_CLK_CTRL 0xfb /* read/write */ # define CEC_FRO_IM_CLK_CTRL_GHOST_DIS (1 << 7) # define CEC_FRO_IM_CLK_CTRL_ENA_OTP (1 << 6) @@ -359,6 +375,7 @@ struct tda998x_priv { # define CEC_RXSHPDLEV_HPD (1 << 1) #define REG_CEC_ENAMODS 0xff /* read/write */ +# define CEC_ENAMODS_EN_CEC_CLK (1 << 7) # define CEC_ENAMODS_DIS_FRO (1 << 6) # define CEC_ENAMODS_DIS_CCLK (1 << 5) # define CEC_ENAMODS_EN_RXSENS (1 << 2) @@ -417,6 +434,114 @@ cec_read(struct tda998x_priv *priv, u8 addr) return val; } +static void cec_enamods(struct tda998x_priv *priv, u8 mods, bool enable) +{ + int val = cec_read(priv, REG_CEC_ENAMODS); + + if (val < 0) + return; + + if (enable) + val |= mods; + else + val &= ~mods; + + cec_write(priv, REG_CEC_ENAMODS, val); +} + +static void tda998x_cec_set_calibration(struct tda998x_priv *priv, bool enable) +{ + if (enable) { + u8 val; + + cec_write(priv, 0xf3, 0xc0); + cec_write(priv, 0xf4, 0xd4); + + /* Enable automatic calibration mode */ + val = cec_read(priv, REG_CEC_DES_FREQ2); + val &= ~CEC_DES_FREQ2_DIS_AUTOCAL; + cec_write(priv, REG_CEC_DES_FREQ2, val); + + /* Enable free running oscillator */ + cec_write(priv, REG_CEC_CLK, CEC_CLK_FRO); + cec_enamods(priv, CEC_ENAMODS_DIS_FRO, false); + + cec_write(priv, REG_CEC_CAL_XOSC_CTRL1, + CEC_CAL_XOSC_CTRL1_ENA_CAL); + } else { + cec_write(priv, REG_CEC_CAL_XOSC_CTRL1, 0); + } +} + +/* + * Calibration for the internal oscillator: we need to set calibration mode, + * and then pulse the IRQ line low for a 10ms ± 1% period. + */ +static void tda998x_cec_calibration(struct tda998x_priv *priv) +{ + struct gpio_desc *calib = priv->calib; + + mutex_lock(&priv->edid_mutex); + if (priv->hdmi->irq > 0) + disable_irq(priv->hdmi->irq); + gpiod_direction_output(calib, 1); + tda998x_cec_set_calibration(priv, true); + + local_irq_disable(); + gpiod_set_value(calib, 0); + mdelay(10); + gpiod_set_value(calib, 1); + local_irq_enable(); + + tda998x_cec_set_calibration(priv, false); + gpiod_direction_input(calib); + if (priv->hdmi->irq > 0) + enable_irq(priv->hdmi->irq); + mutex_unlock(&priv->edid_mutex); +} + +static int tda998x_cec_hook_init(void *data) +{ + struct tda998x_priv *priv = data; + struct gpio_desc *calib; + + calib = gpiod_get(&priv->hdmi->dev, "nxp,calib", GPIOD_ASIS); + if (IS_ERR(calib)) { + dev_warn(&priv->hdmi->dev, "failed to get calibration gpio: %ld\n", + PTR_ERR(calib)); + return PTR_ERR(calib); + } + + priv->calib = calib; + + return 0; +} + +static void tda998x_cec_hook_exit(void *data) +{ + struct tda998x_priv *priv = data; + + gpiod_put(priv->calib); + priv->calib = NULL; +} + +static int tda998x_cec_hook_open(void *data) +{ + struct tda998x_priv *priv = data; + + cec_enamods(priv, CEC_ENAMODS_EN_CEC_CLK | CEC_ENAMODS_EN_CEC, true); + tda998x_cec_calibration(priv); + + return 0; +} + +static void tda998x_cec_hook_release(void *data) +{ + struct tda998x_priv *priv = data; + + cec_enamods(priv, CEC_ENAMODS_EN_CEC_CLK | CEC_ENAMODS_EN_CEC, false); +} + static int set_page(struct tda998x_priv *priv, u16 reg) { @@ -466,13 +591,22 @@ out: return ret; } +#define MAX_WRITE_RANGE_BUF 32 + static void reg_write_range(struct tda998x_priv *priv, u16 reg, u8 *p, int cnt) { struct i2c_client *client = priv->hdmi; - u8 buf[cnt+1]; + /* This is the maximum size of the buffer passed in */ + u8 buf[MAX_WRITE_RANGE_BUF + 1]; int ret; + if (cnt > MAX_WRITE_RANGE_BUF) { + dev_err(&client->dev, "Fixed write buffer too small (%d)\n", + MAX_WRITE_RANGE_BUF); + return; + } + buf[0] = REG2ADDR(reg); memcpy(&buf[1], p, cnt); @@ -630,7 +764,7 @@ static void tda998x_detect_work(struct work_struct *work) { struct tda998x_priv *priv = container_of(work, struct tda998x_priv, detect_work); - struct drm_device *dev = priv->encoder.dev; + struct drm_device *dev = priv->connector.dev; if (dev) drm_kms_helper_hotplug_event(dev); @@ -657,10 +791,13 @@ static irqreturn_t tda998x_irq_thread(int irq, void *data) sta, cec, lvl, flag0, flag1, flag2); if (cec & CEC_RXSHPDINT_HPD) { - if (lvl & CEC_RXSHPDLEV_HPD) + if (lvl & CEC_RXSHPDLEV_HPD) { tda998x_edid_delay_start(priv); - else + } else { schedule_work(&priv->detect_work); + cec_notifier_set_phys_addr(priv->cec_notify, + CEC_PHYS_ADDR_INVALID); + } handled = true; } @@ -679,7 +816,7 @@ static void tda998x_write_if(struct tda998x_priv *priv, u8 bit, u16 addr, union hdmi_infoframe *frame) { - u8 buf[32]; + u8 buf[MAX_WRITE_RANGE_BUF]; ssize_t len; len = hdmi_infoframe_pack(frame, buf, sizeof(buf)); @@ -969,27 +1106,6 @@ static int tda998x_audio_codec_init(struct tda998x_priv *priv, /* DRM connector functions */ -static int tda998x_connector_fill_modes(struct drm_connector *connector, - uint32_t maxX, uint32_t maxY) -{ - struct tda998x_priv *priv = conn_to_tda998x_priv(connector); - int ret; - - mutex_lock(&priv->audio_mutex); - ret = drm_helper_probe_single_connector_modes(connector, maxX, maxY); - - if (connector->edid_blob_ptr) { - struct edid *edid = (void *)connector->edid_blob_ptr->data; - - priv->sink_has_audio = drm_detect_monitor_audio(edid); - } else { - priv->sink_has_audio = false; - } - mutex_unlock(&priv->audio_mutex); - - return ret; -} - static enum drm_connector_status tda998x_connector_detect(struct drm_connector *connector, bool force) { @@ -1008,7 +1124,7 @@ static void tda998x_connector_destroy(struct drm_connector *connector) static const struct drm_connector_funcs tda998x_connector_funcs = { .dpms = drm_helper_connector_dpms, .reset = drm_atomic_helper_connector_reset, - .fill_modes = tda998x_connector_fill_modes, + .fill_modes = drm_helper_probe_single_connector_modes, .detect = tda998x_connector_detect, .destroy = tda998x_connector_destroy, .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state, @@ -1024,6 +1140,8 @@ static int read_edid_block(void *data, u8 *buf, unsigned int blk, size_t length) offset = (blk & 1) ? 128 : 0; segptr = blk / 2; + mutex_lock(&priv->edid_mutex); + reg_write(priv, REG_DDC_ADDR, 0xa0); reg_write(priv, REG_DDC_OFFS, offset); reg_write(priv, REG_DDC_SEGM_ADDR, 0x60); @@ -1043,14 +1161,15 @@ static int read_edid_block(void *data, u8 *buf, unsigned int blk, size_t length) msecs_to_jiffies(100)); if (i < 0) { dev_err(&priv->hdmi->dev, "read edid wait err %d\n", i); - return i; + ret = i; + goto failed; } } else { for (i = 100; i > 0; i--) { msleep(1); ret = reg_read(priv, REG_INT_FLAGS_2); if (ret < 0) - return ret; + goto failed; if (ret & INT_FLAGS_2_EDID_BLK_RD) break; } @@ -1058,17 +1177,22 @@ static int read_edid_block(void *data, u8 *buf, unsigned int blk, size_t length) if (i == 0) { dev_err(&priv->hdmi->dev, "read edid timeout\n"); - return -ETIMEDOUT; + ret = -ETIMEDOUT; + goto failed; } ret = reg_read_range(priv, REG_EDID_DATA_0, buf, length); if (ret != length) { dev_err(&priv->hdmi->dev, "failed to read edid block %d: %d\n", blk, ret); - return ret; + goto failed; } - return 0; + ret = 0; + + failed: + mutex_unlock(&priv->edid_mutex); + return ret; } static int tda998x_connector_get_modes(struct drm_connector *connector) @@ -1098,41 +1222,30 @@ static int tda998x_connector_get_modes(struct drm_connector *connector) return 0; } - drm_mode_connector_update_edid_property(connector, edid); + drm_connector_update_edid_property(connector, edid); + cec_notifier_set_phys_addr_from_edid(priv->cec_notify, edid); + + mutex_lock(&priv->audio_mutex); n = drm_add_edid_modes(connector, edid); + priv->sink_has_audio = drm_detect_monitor_audio(edid); + mutex_unlock(&priv->audio_mutex); kfree(edid); return n; } -static int tda998x_connector_mode_valid(struct drm_connector *connector, - struct drm_display_mode *mode) -{ - /* TDA19988 dotclock can go up to 165MHz */ - struct tda998x_priv *priv = conn_to_tda998x_priv(connector); - - if (mode->clock > ((priv->rev == TDA19988) ? 165000 : 150000)) - return MODE_CLOCK_HIGH; - if (mode->htotal >= BIT(13)) - return MODE_BAD_HVALUE; - if (mode->vtotal >= BIT(11)) - return MODE_BAD_VVALUE; - return MODE_OK; -} - static struct drm_encoder * tda998x_connector_best_encoder(struct drm_connector *connector) { struct tda998x_priv *priv = conn_to_tda998x_priv(connector); - return &priv->encoder; + return priv->bridge.encoder; } static const struct drm_connector_helper_funcs tda998x_connector_helper_funcs = { .get_modes = tda998x_connector_get_modes, - .mode_valid = tda998x_connector_mode_valid, .best_encoder = tda998x_connector_best_encoder, }; @@ -1156,25 +1269,48 @@ static int tda998x_connector_init(struct tda998x_priv *priv, if (ret) return ret; - drm_mode_connector_attach_encoder(&priv->connector, &priv->encoder); + drm_connector_attach_encoder(&priv->connector, + priv->bridge.encoder); return 0; } -/* DRM encoder functions */ +/* DRM bridge functions */ -static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode) +static int tda998x_bridge_attach(struct drm_bridge *bridge) { - struct tda998x_priv *priv = enc_to_tda998x_priv(encoder); - bool on; + struct tda998x_priv *priv = bridge_to_tda998x_priv(bridge); + + return tda998x_connector_init(priv, bridge->dev); +} - /* we only care about on or off: */ - on = mode == DRM_MODE_DPMS_ON; +static void tda998x_bridge_detach(struct drm_bridge *bridge) +{ + struct tda998x_priv *priv = bridge_to_tda998x_priv(bridge); - if (on == priv->is_on) - return; + drm_connector_cleanup(&priv->connector); +} - if (on) { +static enum drm_mode_status tda998x_bridge_mode_valid(struct drm_bridge *bridge, + const struct drm_display_mode *mode) +{ + /* TDA19988 dotclock can go up to 165MHz */ + struct tda998x_priv *priv = bridge_to_tda998x_priv(bridge); + + if (mode->clock > ((priv->rev == TDA19988) ? 165000 : 150000)) + return MODE_CLOCK_HIGH; + if (mode->htotal >= BIT(13)) + return MODE_BAD_HVALUE; + if (mode->vtotal >= BIT(11)) + return MODE_BAD_VVALUE; + return MODE_OK; +} + +static void tda998x_bridge_enable(struct drm_bridge *bridge) +{ + struct tda998x_priv *priv = bridge_to_tda998x_priv(bridge); + + if (!priv->is_on) { /* enable video ports, audio will be enabled later */ reg_write(priv, REG_ENA_VP_0, 0xff); reg_write(priv, REG_ENA_VP_1, 0xff); @@ -1185,7 +1321,14 @@ static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode) reg_write(priv, REG_VIP_CNTRL_2, priv->vip_cntrl_2); priv->is_on = true; - } else { + } +} + +static void tda998x_bridge_disable(struct drm_bridge *bridge) +{ + struct tda998x_priv *priv = bridge_to_tda998x_priv(bridge); + + if (priv->is_on) { /* disable video ports */ reg_write(priv, REG_ENA_VP_0, 0x00); reg_write(priv, REG_ENA_VP_1, 0x00); @@ -1195,12 +1338,12 @@ static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode) } } -static void -tda998x_encoder_mode_set(struct drm_encoder *encoder, - struct drm_display_mode *mode, - struct drm_display_mode *adjusted_mode) +static void tda998x_bridge_mode_set(struct drm_bridge *bridge, + struct drm_display_mode *mode, + struct drm_display_mode *adjusted_mode) { - struct tda998x_priv *priv = enc_to_tda998x_priv(encoder); + struct tda998x_priv *priv = bridge_to_tda998x_priv(bridge); + unsigned long tmds_clock; u16 ref_pix, ref_line, n_pix, n_line; u16 hs_pix_s, hs_pix_e; u16 vs1_pix_s, vs1_pix_e, vs1_line_s, vs1_line_e; @@ -1271,12 +1414,19 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder, (mode->vsync_end - mode->vsync_start)/2; } - div = 148500 / mode->clock; - if (div != 0) { - div--; - if (div > 3) - div = 3; - } + tmds_clock = mode->clock; + + /* + * The divisor is power-of-2. The TDA9983B datasheet gives + * this as ranges of Msample/s, which is 10x the TMDS clock: + * 0 - 800 to 1500 Msample/s + * 1 - 400 to 800 Msample/s + * 2 - 200 to 400 Msample/s + * 3 - as 2 above + */ + for (div = 0; div < 3; div++) + if (80000 >> div <= tmds_clock) + break; mutex_lock(&priv->audio_mutex); @@ -1407,23 +1557,14 @@ tda998x_encoder_mode_set(struct drm_encoder *encoder, mutex_unlock(&priv->audio_mutex); } -static void tda998x_destroy(struct tda998x_priv *priv) -{ - /* disable all IRQs and free the IRQ handler */ - cec_write(priv, REG_CEC_RXSHPDINTENA, 0); - reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); - - if (priv->audio_pdev) - platform_device_unregister(priv->audio_pdev); - - if (priv->hdmi->irq) - free_irq(priv->hdmi->irq, priv); - - del_timer_sync(&priv->edid_delay_timer); - cancel_work_sync(&priv->detect_work); - - i2c_unregister_device(priv->cec); -} +static const struct drm_bridge_funcs tda998x_bridge_funcs = { + .attach = tda998x_bridge_attach, + .detach = tda998x_bridge_detach, + .mode_valid = tda998x_bridge_mode_valid, + .disable = tda998x_bridge_disable, + .mode_set = tda998x_bridge_mode_set, + .enable = tda998x_bridge_enable, +}; /* I2C driver functions */ @@ -1469,14 +1610,69 @@ static int tda998x_get_audio_ports(struct tda998x_priv *priv, return 0; } -static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) +static void tda998x_set_config(struct tda998x_priv *priv, + const struct tda998x_encoder_params *p) +{ + priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(p->swap_a) | + (p->mirr_a ? VIP_CNTRL_0_MIRR_A : 0) | + VIP_CNTRL_0_SWAP_B(p->swap_b) | + (p->mirr_b ? VIP_CNTRL_0_MIRR_B : 0); + priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(p->swap_c) | + (p->mirr_c ? VIP_CNTRL_1_MIRR_C : 0) | + VIP_CNTRL_1_SWAP_D(p->swap_d) | + (p->mirr_d ? VIP_CNTRL_1_MIRR_D : 0); + priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(p->swap_e) | + (p->mirr_e ? VIP_CNTRL_2_MIRR_E : 0) | + VIP_CNTRL_2_SWAP_F(p->swap_f) | + (p->mirr_f ? VIP_CNTRL_2_MIRR_F : 0); + + priv->audio_params = p->audio_params; +} + +static void tda998x_destroy(struct device *dev) +{ + struct tda998x_priv *priv = dev_get_drvdata(dev); + + drm_bridge_remove(&priv->bridge); + + /* disable all IRQs and free the IRQ handler */ + cec_write(priv, REG_CEC_RXSHPDINTENA, 0); + reg_clear(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); + + if (priv->audio_pdev) + platform_device_unregister(priv->audio_pdev); + + if (priv->hdmi->irq) + free_irq(priv->hdmi->irq, priv); + + del_timer_sync(&priv->edid_delay_timer); + cancel_work_sync(&priv->detect_work); + + i2c_unregister_device(priv->cec); + + if (priv->cec_notify) + cec_notifier_put(priv->cec_notify); +} + +static int tda998x_create(struct device *dev) { + struct i2c_client *client = to_i2c_client(dev); struct device_node *np = client->dev.of_node; + struct i2c_board_info cec_info; + struct tda998x_priv *priv; u32 video; int rev_lo, rev_hi, ret; + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(dev, priv); + mutex_init(&priv->mutex); /* protect the page access */ mutex_init(&priv->audio_mutex); /* protect access from audio thread */ + mutex_init(&priv->edid_mutex); + INIT_LIST_HEAD(&priv->bridge.list); init_waitqueue_head(&priv->edid_delay_waitq); timer_setup(&priv->edid_delay_timer, tda998x_edid_delay_done, 0); INIT_WORK(&priv->detect_work, tda998x_detect_work); @@ -1499,13 +1695,13 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) /* read version: */ rev_lo = reg_read(priv, REG_VERSION_LSB); if (rev_lo < 0) { - dev_err(&client->dev, "failed to read version: %d\n", rev_lo); + dev_err(dev, "failed to read version: %d\n", rev_lo); return rev_lo; } rev_hi = reg_read(priv, REG_VERSION_MSB); if (rev_hi < 0) { - dev_err(&client->dev, "failed to read version: %d\n", rev_hi); + dev_err(dev, "failed to read version: %d\n", rev_hi); return rev_hi; } @@ -1516,20 +1712,19 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) switch (priv->rev) { case TDA9989N2: - dev_info(&client->dev, "found TDA9989 n2"); + dev_info(dev, "found TDA9989 n2"); break; case TDA19989: - dev_info(&client->dev, "found TDA19989"); + dev_info(dev, "found TDA19989"); break; case TDA19989N2: - dev_info(&client->dev, "found TDA19989 n2"); + dev_info(dev, "found TDA19989 n2"); break; case TDA19988: - dev_info(&client->dev, "found TDA19988"); + dev_info(dev, "found TDA19988"); break; default: - dev_err(&client->dev, "found unsupported device: %04x\n", - priv->rev); + dev_err(dev, "found unsupported device: %04x\n", priv->rev); return -ENXIO; } @@ -1564,13 +1759,15 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) irq_flags = irqd_get_trigger_type(irq_get_irq_data(client->irq)); + + priv->cec_glue.irq_flags = irq_flags; + irq_flags |= IRQF_SHARED | IRQF_ONESHOT; ret = request_threaded_irq(client->irq, NULL, tda998x_irq_thread, irq_flags, "tda998x", priv); if (ret) { - dev_err(&client->dev, - "failed to request IRQ#%u: %d\n", + dev_err(dev, "failed to request IRQ#%u: %d\n", client->irq, ret); goto err_irq; } @@ -1579,7 +1776,34 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) cec_write(priv, REG_CEC_RXSHPDINTENA, CEC_RXSHPDLEV_HPD); } - priv->cec = i2c_new_dummy(client->adapter, priv->cec_addr); + priv->cec_notify = cec_notifier_get(dev); + if (!priv->cec_notify) { + ret = -ENOMEM; + goto fail; + } + + priv->cec_glue.parent = dev; + priv->cec_glue.data = priv; + priv->cec_glue.init = tda998x_cec_hook_init; + priv->cec_glue.exit = tda998x_cec_hook_exit; + priv->cec_glue.open = tda998x_cec_hook_open; + priv->cec_glue.release = tda998x_cec_hook_release; + + /* + * Some TDA998x are actually two I2C devices merged onto one piece + * of silicon: TDA9989 and TDA19989 combine the HDMI transmitter + * with a slightly modified TDA9950 CEC device. The CEC device + * is at the TDA9950 address, with the address pins strapped across + * to the TDA998x address pins. Hence, it always has the same + * offset. + */ + memset(&cec_info, 0, sizeof(cec_info)); + strlcpy(cec_info.type, "tda9950", sizeof(cec_info.type)); + cec_info.addr = priv->cec_addr; + cec_info.platform_data = &priv->cec_glue; + cec_info.irq = client->irq; + + priv->cec = i2c_new_device(client->adapter, &cec_info); if (!priv->cec) { ret = -ENODEV; goto fail; @@ -1588,56 +1812,44 @@ static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv) /* enable EDID read irq: */ reg_set(priv, REG_INT_FLAGS_2, INT_FLAGS_2_EDID_BLK_RD); - if (!np) - return 0; /* non-DT */ + if (np) { + /* get the device tree parameters */ + ret = of_property_read_u32(np, "video-ports", &video); + if (ret == 0) { + priv->vip_cntrl_0 = video >> 16; + priv->vip_cntrl_1 = video >> 8; + priv->vip_cntrl_2 = video; + } + + ret = tda998x_get_audio_ports(priv, np); + if (ret) + goto fail; - /* get the device tree parameters */ - ret = of_property_read_u32(np, "video-ports", &video); - if (ret == 0) { - priv->vip_cntrl_0 = video >> 16; - priv->vip_cntrl_1 = video >> 8; - priv->vip_cntrl_2 = video; + if (priv->audio_port[0].format != AFMT_UNUSED) + tda998x_audio_codec_init(priv, &client->dev); + } else if (dev->platform_data) { + tda998x_set_config(priv, dev->platform_data); } - ret = tda998x_get_audio_ports(priv, np); - if (ret) - goto fail; + priv->bridge.funcs = &tda998x_bridge_funcs; +#ifdef CONFIG_OF + priv->bridge.of_node = dev->of_node; +#endif - if (priv->audio_port[0].format != AFMT_UNUSED) - tda998x_audio_codec_init(priv, &client->dev); + drm_bridge_add(&priv->bridge); return 0; fail: - if (client->irq) - free_irq(client->irq, priv); + tda998x_destroy(dev); err_irq: - i2c_unregister_device(priv->cec); return ret; } -static void tda998x_encoder_prepare(struct drm_encoder *encoder) -{ - tda998x_encoder_dpms(encoder, DRM_MODE_DPMS_OFF); -} - -static void tda998x_encoder_commit(struct drm_encoder *encoder) -{ - tda998x_encoder_dpms(encoder, DRM_MODE_DPMS_ON); -} - -static const struct drm_encoder_helper_funcs tda998x_encoder_helper_funcs = { - .dpms = tda998x_encoder_dpms, - .prepare = tda998x_encoder_prepare, - .commit = tda998x_encoder_commit, - .mode_set = tda998x_encoder_mode_set, -}; +/* DRM encoder functions */ static void tda998x_encoder_destroy(struct drm_encoder *encoder) { - struct tda998x_priv *priv = enc_to_tda998x_priv(encoder); - - tda998x_destroy(priv); drm_encoder_cleanup(encoder); } @@ -1645,40 +1857,12 @@ static const struct drm_encoder_funcs tda998x_encoder_funcs = { .destroy = tda998x_encoder_destroy, }; -static void tda998x_set_config(struct tda998x_priv *priv, - const struct tda998x_encoder_params *p) +static int tda998x_encoder_init(struct device *dev, struct drm_device *drm) { - priv->vip_cntrl_0 = VIP_CNTRL_0_SWAP_A(p->swap_a) | - (p->mirr_a ? VIP_CNTRL_0_MIRR_A : 0) | - VIP_CNTRL_0_SWAP_B(p->swap_b) | - (p->mirr_b ? VIP_CNTRL_0_MIRR_B : 0); - priv->vip_cntrl_1 = VIP_CNTRL_1_SWAP_C(p->swap_c) | - (p->mirr_c ? VIP_CNTRL_1_MIRR_C : 0) | - VIP_CNTRL_1_SWAP_D(p->swap_d) | - (p->mirr_d ? VIP_CNTRL_1_MIRR_D : 0); - priv->vip_cntrl_2 = VIP_CNTRL_2_SWAP_E(p->swap_e) | - (p->mirr_e ? VIP_CNTRL_2_MIRR_E : 0) | - VIP_CNTRL_2_SWAP_F(p->swap_f) | - (p->mirr_f ? VIP_CNTRL_2_MIRR_F : 0); - - priv->audio_params = p->audio_params; -} - -static int tda998x_bind(struct device *dev, struct device *master, void *data) -{ - struct tda998x_encoder_params *params = dev->platform_data; - struct i2c_client *client = to_i2c_client(dev); - struct drm_device *drm = data; - struct tda998x_priv *priv; + struct tda998x_priv *priv = dev_get_drvdata(dev); u32 crtcs = 0; int ret; - priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); - if (!priv) - return -ENOMEM; - - dev_set_drvdata(dev, priv); - if (dev->of_node) crtcs = drm_of_find_possible_crtcs(drm, dev->of_node); @@ -1690,40 +1874,36 @@ static int tda998x_bind(struct device *dev, struct device *master, void *data) priv->encoder.possible_crtcs = crtcs; - ret = tda998x_create(client, priv); - if (ret) - return ret; - - if (!dev->of_node && params) - tda998x_set_config(priv, params); - - drm_encoder_helper_add(&priv->encoder, &tda998x_encoder_helper_funcs); ret = drm_encoder_init(drm, &priv->encoder, &tda998x_encoder_funcs, DRM_MODE_ENCODER_TMDS, NULL); if (ret) goto err_encoder; - ret = tda998x_connector_init(priv, drm); + ret = drm_bridge_attach(&priv->encoder, &priv->bridge, NULL); if (ret) - goto err_connector; + goto err_bridge; return 0; -err_connector: +err_bridge: drm_encoder_cleanup(&priv->encoder); err_encoder: - tda998x_destroy(priv); return ret; } +static int tda998x_bind(struct device *dev, struct device *master, void *data) +{ + struct drm_device *drm = data; + + return tda998x_encoder_init(dev, drm); +} + static void tda998x_unbind(struct device *dev, struct device *master, void *data) { struct tda998x_priv *priv = dev_get_drvdata(dev); - drm_connector_cleanup(&priv->connector); drm_encoder_cleanup(&priv->encoder); - tda998x_destroy(priv); } static const struct component_ops tda998x_ops = { @@ -1734,16 +1914,27 @@ static const struct component_ops tda998x_ops = { static int tda998x_probe(struct i2c_client *client, const struct i2c_device_id *id) { + int ret; + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { dev_warn(&client->dev, "adapter does not support I2C\n"); return -EIO; } - return component_add(&client->dev, &tda998x_ops); + + ret = tda998x_create(&client->dev); + if (ret) + return ret; + + ret = component_add(&client->dev, &tda998x_ops); + if (ret) + tda998x_destroy(&client->dev); + return ret; } static int tda998x_remove(struct i2c_client *client) { component_del(&client->dev, &tda998x_ops); + tda998x_destroy(&client->dev); return 0; } |