Merge branch 'drm-tda998x-mali' of git://git.armlinux.org.uk/~rmk/linux-arm into...
authorDave Airlie <airlied@redhat.com>
Wed, 16 Nov 2016 22:55:26 +0000 (08:55 +1000)
committerDave Airlie <airlied@redhat.com>
Wed, 16 Nov 2016 22:55:26 +0000 (08:55 +1000)
Fix conncector registration with tda998x.

* 'drm-tda998x-mali' of git://git.armlinux.org.uk/~rmk/linux-arm:
  drm/i2c: tda998x: mali-dp: hdlcd: refactor connector registration

1  2 
drivers/gpu/drm/arm/hdlcd_drv.c
drivers/gpu/drm/arm/malidp_drv.c
drivers/gpu/drm/i2c/tda998x_drv.c

index 59747ecaad54afea71305331a050f14983700154,85aec8b86d898de6c6abc4bedff864c9f8b93772..faab7f9bd3b75bdacd99755c1bf9e567439aadfb
@@@ -268,7 -268,9 +268,7 @@@ static const struct file_operations fop
        .open           = drm_open,
        .release        = drm_release,
        .unlocked_ioctl = drm_ioctl,
 -#ifdef CONFIG_COMPAT
        .compat_ioctl   = drm_compat_ioctl,
 -#endif
        .poll           = drm_poll,
        .read           = drm_read,
        .llseek         = noop_llseek,
@@@ -324,8 -326,8 +324,8 @@@ static int hdlcd_drm_bind(struct devic
                return -ENOMEM;
  
        drm = drm_dev_alloc(&hdlcd_driver, dev);
 -      if (!drm)
 -              return -ENOMEM;
 +      if (IS_ERR(drm))
 +              return PTR_ERR(drm);
  
        drm->dev_private = hdlcd;
        dev_set_drvdata(dev, drm);
        if (ret)
                goto err_free;
  
-       ret = drm_dev_register(drm, 0);
-       if (ret)
-               goto err_unload;
        ret = component_bind_all(dev, drm);
        if (ret) {
                DRM_ERROR("Failed to bind all components\n");
-               goto err_unregister;
+               goto err_unload;
        }
  
        ret = pm_runtime_set_active(dev);
                goto err_fbdev;
        }
  
+       ret = drm_dev_register(drm, 0);
+       if (ret)
+               goto err_register;
        return 0;
  
+ err_register:
+       if (hdlcd->fbdev) {
+               drm_fbdev_cma_fini(hdlcd->fbdev);
+               hdlcd->fbdev = NULL;
+       }
  err_fbdev:
        drm_kms_helper_poll_fini(drm);
        drm_mode_config_cleanup(drm);
@@@ -379,8 -386,6 +384,6 @@@ err_vblank
        pm_runtime_disable(drm->dev);
  err_pm_active:
        component_unbind_all(dev, drm);
- err_unregister:
-       drm_dev_unregister(drm);
  err_unload:
        drm_irq_uninstall(drm);
        of_reserved_mem_device_release(drm->dev);
@@@ -396,6 -401,7 +399,7 @@@ static void hdlcd_drm_unbind(struct dev
        struct drm_device *drm = dev_get_drvdata(dev);
        struct hdlcd_drm_private *hdlcd = drm->dev_private;
  
+       drm_dev_unregister(drm);
        if (hdlcd->fbdev) {
                drm_fbdev_cma_fini(hdlcd->fbdev);
                hdlcd->fbdev = NULL;
        pm_runtime_disable(drm->dev);
        of_reserved_mem_device_release(drm->dev);
        drm_mode_config_cleanup(drm);
-       drm_dev_unregister(drm);
        drm_dev_unref(drm);
        drm->dev_private = NULL;
        dev_set_drvdata(dev, NULL);
@@@ -451,8 -456,7 +454,8 @@@ static int hdlcd_probe(struct platform_
                return -EAGAIN;
        }
  
 -      component_match_add(&pdev->dev, &match, compare_dev, port);
 +      drm_of_component_match_add(&pdev->dev, &match, compare_dev, port);
 +      of_node_put(port);
  
        return component_master_add_with_match(&pdev->dev, &hdlcd_master_ops,
                                               match);
index ebf90c794eabc4e34fb4ac6b4b7ab0343656006b,79bfc1360a1bec758fef09fead6f69e530d5eade..32f746e313790499e031e9709765f35ebc28b27e
@@@ -42,7 -42,6 +42,7 @@@ static int malidp_set_and_wait_config_v
        struct malidp_hw_device *hwdev = malidp->dev;
        int ret;
  
 +      atomic_set(&malidp->config_valid, 0);
        hwdev->set_config_valid(hwdev);
        /* don't wait for config_valid flag if we are in config mode */
        if (hwdev->in_config_mode(hwdev))
@@@ -92,7 -91,7 +92,7 @@@ static void malidp_atomic_commit_tail(s
  
        drm_atomic_helper_commit_modeset_disables(drm, state);
        drm_atomic_helper_commit_modeset_enables(drm, state);
 -      drm_atomic_helper_commit_planes(drm, state, true);
 +      drm_atomic_helper_commit_planes(drm, state, 0);
  
        malidp_atomic_commit_hw_done(state);
  
@@@ -155,12 -154,6 +155,12 @@@ static int malidp_init(struct drm_devic
        return 0;
  }
  
 +static void malidp_fini(struct drm_device *drm)
 +{
 +      malidp_de_planes_destroy(drm);
 +      drm_mode_config_cleanup(drm);
 +}
 +
  static int malidp_irq_init(struct platform_device *pdev)
  {
        int irq_de, irq_se, ret = 0;
@@@ -203,7 -196,9 +203,7 @@@ static const struct file_operations fop
        .open = drm_open,
        .release = drm_release,
        .unlocked_ioctl = drm_ioctl,
 -#ifdef CONFIG_COMPAT
        .compat_ioctl = drm_compat_ioctl,
 -#endif
        .poll = drm_poll,
        .read = drm_read,
        .llseek = noop_llseek,
@@@ -315,8 -310,8 +315,8 @@@ static int malidp_bind(struct device *d
                return ret;
  
        drm = drm_dev_alloc(&malidp_driver, dev);
 -      if (!drm) {
 -              ret = -ENOMEM;
 +      if (IS_ERR(drm)) {
 +              ret = PTR_ERR(drm);
                goto alloc_fail;
        }
  
        if (ret < 0)
                goto init_fail;
  
-       ret = drm_dev_register(drm, 0);
-       if (ret)
-               goto register_fail;
        /* Set the CRTC's port so that the encoder component can find it */
        ep = of_graph_get_next_endpoint(dev->of_node, NULL);
        if (!ep) {
        if (ret < 0)
                goto irq_init_fail;
  
 +      drm->irq_enabled = true;
 +
        ret = drm_vblank_init(drm, drm->mode_config.num_crtc);
        if (ret < 0) {
                DRM_ERROR("failed to initialise vblank\n");
        }
  
        drm_kms_helper_poll_init(drm);
+       ret = drm_dev_register(drm, 0);
+       if (ret)
+               goto register_fail;
        return 0;
  
+ register_fail:
+       if (malidp->fbdev) {
+               drm_fbdev_cma_fini(malidp->fbdev);
+               malidp->fbdev = NULL;
+       }
  fbdev_fail:
        drm_vblank_cleanup(drm);
  vblank_fail:
        malidp_se_irq_fini(drm);
        malidp_de_irq_fini(drm);
 +      drm->irq_enabled = false;
  irq_init_fail:
        component_unbind_all(dev, drm);
  bind_fail:
        of_node_put(malidp->crtc.port);
        malidp->crtc.port = NULL;
  port_fail:
-       drm_dev_unregister(drm);
- register_fail:
 -      malidp_de_planes_destroy(drm);
 -      drm_mode_config_cleanup(drm);
 +      malidp_fini(drm);
  init_fail:
        drm->dev_private = NULL;
        dev_set_drvdata(dev, NULL);
@@@ -438,6 -435,7 +442,7 @@@ static void malidp_unbind(struct devic
        struct malidp_drm *malidp = drm->dev_private;
        struct malidp_hw_device *hwdev = malidp->dev;
  
+       drm_dev_unregister(drm);
        if (malidp->fbdev) {
                drm_fbdev_cma_fini(malidp->fbdev);
                malidp->fbdev = NULL;
        component_unbind_all(dev, drm);
        of_node_put(malidp->crtc.port);
        malidp->crtc.port = NULL;
-       drm_dev_unregister(drm);
 -      malidp_de_planes_destroy(drm);
 -      drm_mode_config_cleanup(drm);
 +      malidp_fini(drm);
        drm->dev_private = NULL;
        dev_set_drvdata(dev, NULL);
        clk_disable_unprepare(hwdev->mclk);
@@@ -498,9 -496,7 +502,9 @@@ static int malidp_platform_probe(struc
                return -EAGAIN;
        }
  
 -      component_match_add(&pdev->dev, &match, malidp_compare_dev, port);
 +      drm_of_component_match_add(&pdev->dev, &match, malidp_compare_dev,
 +                                 port);
 +      of_node_put(port);
        return component_master_add_with_match(&pdev->dev, &malidp_master_ops,
                                               match);
  }
index af8683e0dd5480b0088e4ce5895592b5a9d584cf,6e6fca20ba8b46e1d51d99b5f1cc99f6cf24983e..027521f30b6ebc3f1c5bf1f9afa5f77a91b8a2c1
@@@ -20,7 -20,6 +20,7 @@@
  #include <linux/module.h>
  #include <linux/irq.h>
  #include <sound/asoundef.h>
 +#include <sound/hdmi-codec.h>
  
  #include <drm/drmP.h>
  #include <drm/drm_atomic_helper.h>
  
  #define DBG(fmt, ...) DRM_DEBUG(fmt"\n", ##__VA_ARGS__)
  
 +struct tda998x_audio_port {
 +      u8 format;              /* AFMT_xxx */
 +      u8 config;              /* AP value */
 +};
 +
  struct tda998x_priv {
        struct i2c_client *cec;
        struct i2c_client *hdmi;
        u8 vip_cntrl_0;
        u8 vip_cntrl_1;
        u8 vip_cntrl_2;
 -      struct tda998x_encoder_params params;
 +      struct tda998x_audio_params audio_params;
 +
 +      struct platform_device *audio_pdev;
 +      struct mutex audio_mutex;
  
        wait_queue_head_t wq_edid;
        volatile int wq_edid_wait;
@@@ -62,8 -53,6 +62,8 @@@
  
        struct drm_encoder encoder;
        struct drm_connector connector;
 +
 +      struct tda998x_audio_port audio_port[2];
  };
  
  #define conn_to_tda998x_priv(x) \
@@@ -677,16 -666,26 +677,16 @@@ tda998x_write_if(struct tda998x_priv *p
        reg_set(priv, REG_DIP_IF_FLAGS, bit);
  }
  
 -static void
 -tda998x_write_aif(struct tda998x_priv *priv, struct tda998x_encoder_params *p)
 +static int tda998x_write_aif(struct tda998x_priv *priv,
 +                           struct hdmi_audio_infoframe *cea)
  {
        union hdmi_infoframe frame;
  
 -      hdmi_audio_infoframe_init(&frame.audio);
 -
 -      frame.audio.channels = p->audio_frame[1] & 0x07;
 -      frame.audio.channel_allocation = p->audio_frame[4];
 -      frame.audio.level_shift_value = (p->audio_frame[5] & 0x78) >> 3;
 -      frame.audio.downmix_inhibit = (p->audio_frame[5] & 0x80) >> 7;
 -
 -      /*
 -       * L-PCM and IEC61937 compressed audio shall always set sample
 -       * frequency to "refer to stream".  For others, see the HDMI
 -       * specification.
 -       */
 -      frame.audio.sample_frequency = (p->audio_frame[2] & 0x1c) >> 2;
 +      frame.audio = *cea;
  
        tda998x_write_if(priv, DIP_IF_FLAGS_IF4, REG_IF4_HB0, &frame);
 +
 +      return 0;
  }
  
  static void
@@@ -711,21 -710,20 +711,21 @@@ static void tda998x_audio_mute(struct t
        }
  }
  
 -static void
 +static int
  tda998x_configure_audio(struct tda998x_priv *priv,
 -              struct drm_display_mode *mode, struct tda998x_encoder_params *p)
 +                      struct tda998x_audio_params *params,
 +                      unsigned mode_clock)
  {
        u8 buf[6], clksel_aip, clksel_fs, cts_n, adiv;
        u32 n;
  
        /* Enable audio ports */
 -      reg_write(priv, REG_ENA_AP, p->audio_cfg);
 -      reg_write(priv, REG_ENA_ACLK, p->audio_clk_cfg);
 +      reg_write(priv, REG_ENA_AP, params->config);
  
        /* Set audio input source */
 -      switch (p->audio_format) {
 +      switch (params->format) {
        case AFMT_SPDIF:
 +              reg_write(priv, REG_ENA_ACLK, 0);
                reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_SPDIF);
                clksel_aip = AIP_CLKSEL_AIP_SPDIF;
                clksel_fs = AIP_CLKSEL_FS_FS64SPDIF;
                break;
  
        case AFMT_I2S:
 +              reg_write(priv, REG_ENA_ACLK, 1);
                reg_write(priv, REG_MUX_AP, MUX_AP_SELECT_I2S);
                clksel_aip = AIP_CLKSEL_AIP_I2S;
                clksel_fs = AIP_CLKSEL_FS_ACLK;
 -              cts_n = CTS_N_M(3) | CTS_N_K(3);
 +              switch (params->sample_width) {
 +              case 16:
 +                      cts_n = CTS_N_M(3) | CTS_N_K(1);
 +                      break;
 +              case 18:
 +              case 20:
 +              case 24:
 +                      cts_n = CTS_N_M(3) | CTS_N_K(2);
 +                      break;
 +              default:
 +              case 32:
 +                      cts_n = CTS_N_M(3) | CTS_N_K(3);
 +                      break;
 +              }
                break;
  
        default:
 -              BUG();
 -              return;
 +              dev_err(&priv->hdmi->dev, "Unsupported I2S format\n");
 +              return -EINVAL;
        }
  
        reg_write(priv, REG_AIP_CLKSEL, clksel_aip);
         * assume 100MHz requires larger divider.
         */
        adiv = AUDIO_DIV_SERCLK_8;
 -      if (mode->clock > 100000)
 +      if (mode_clock > 100000)
                adiv++;                 /* AUDIO_DIV_SERCLK_16 */
  
        /* S/PDIF asks for a larger divider */
 -      if (p->audio_format == AFMT_SPDIF)
 +      if (params->format == AFMT_SPDIF)
                adiv++;                 /* AUDIO_DIV_SERCLK_16 or _32 */
  
        reg_write(priv, REG_AUDIO_DIV, adiv);
         * This is the approximate value of N, which happens to be
         * the recommended values for non-coherent clocks.
         */
 -      n = 128 * p->audio_sample_rate / 1000;
 +      n = 128 * params->sample_rate / 1000;
  
        /* Write the CTS and N values */
        buf[0] = 0x44;
        reg_set(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS);
        reg_clear(priv, REG_AIP_CNTRL_0, AIP_CNTRL_0_RST_CTS);
  
 -      /* Write the channel status */
 -      buf[0] = IEC958_AES0_CON_NOT_COPYRIGHT;
 -      buf[1] = 0x00;
 -      buf[2] = IEC958_AES3_CON_FS_NOTID;
 -      buf[3] = IEC958_AES4_CON_ORIGFS_NOTID |
 -                      IEC958_AES4_CON_MAX_WORDLEN_24;
 +      /* Write the channel status
 +       * The REG_CH_STAT_B-registers skip IEC958 AES2 byte, because
 +       * there is a separate register for each I2S wire.
 +       */
 +      buf[0] = params->status[0];
 +      buf[1] = params->status[1];
 +      buf[2] = params->status[3];
 +      buf[3] = params->status[4];
        reg_write_range(priv, REG_CH_STAT_B(0), buf, 4);
  
        tda998x_audio_mute(priv, true);
        msleep(20);
        tda998x_audio_mute(priv, false);
  
 -      /* Write the audio information packet */
 -      tda998x_write_aif(priv, p);
 +      return tda998x_write_aif(priv, &params->cea);
  }
  
  /* DRM encoder functions */
@@@ -837,7 -820,7 +837,7 @@@ static void tda998x_encoder_set_config(
                            VIP_CNTRL_2_SWAP_F(p->swap_f) |
                            (p->mirr_f ? VIP_CNTRL_2_MIRR_F : 0);
  
 -      priv->params = *p;
 +      priv->audio_params = p->audio_params;
  }
  
  static void tda998x_encoder_dpms(struct drm_encoder *encoder, int mode)
@@@ -1074,13 -1057,9 +1074,13 @@@ tda998x_encoder_mode_set(struct drm_enc
  
                tda998x_write_avi(priv, adjusted_mode);
  
 -              if (priv->params.audio_cfg)
 -                      tda998x_configure_audio(priv, adjusted_mode,
 -                                              &priv->params);
 +              if (priv->audio_params.format != AFMT_UNUSED) {
 +                      mutex_lock(&priv->audio_mutex);
 +                      tda998x_configure_audio(priv,
 +                                              &priv->audio_params,
 +                                              adjusted_mode->clock);
 +                      mutex_unlock(&priv->audio_mutex);
 +              }
        }
  }
  
@@@ -1180,8 -1159,6 +1180,8 @@@ static int tda998x_connector_get_modes(
        drm_mode_connector_update_edid_property(connector, edid);
        n = drm_add_edid_modes(connector, edid);
        priv->is_hdmi_sink = drm_detect_hdmi_monitor(edid);
 +      drm_edid_to_eld(connector, edid);
 +
        kfree(edid);
  
        return n;
@@@ -1203,9 -1180,6 +1203,9 @@@ static void tda998x_destroy(struct tda9
        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);
  
        i2c_unregister_device(priv->cec);
  }
  
 +static int tda998x_audio_hw_params(struct device *dev, void *data,
 +                                 struct hdmi_codec_daifmt *daifmt,
 +                                 struct hdmi_codec_params *params)
 +{
 +      struct tda998x_priv *priv = dev_get_drvdata(dev);
 +      int i, ret;
 +      struct tda998x_audio_params audio = {
 +              .sample_width = params->sample_width,
 +              .sample_rate = params->sample_rate,
 +              .cea = params->cea,
 +      };
 +
 +      if (!priv->encoder.crtc)
 +              return -ENODEV;
 +
 +      memcpy(audio.status, params->iec.status,
 +             min(sizeof(audio.status), sizeof(params->iec.status)));
 +
 +      switch (daifmt->fmt) {
 +      case HDMI_I2S:
 +              if (daifmt->bit_clk_inv || daifmt->frame_clk_inv ||
 +                  daifmt->bit_clk_master || daifmt->frame_clk_master) {
 +                      dev_err(dev, "%s: Bad flags %d %d %d %d\n", __func__,
 +                              daifmt->bit_clk_inv, daifmt->frame_clk_inv,
 +                              daifmt->bit_clk_master,
 +                              daifmt->frame_clk_master);
 +                      return -EINVAL;
 +              }
 +              for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++)
 +                      if (priv->audio_port[i].format == AFMT_I2S)
 +                              audio.config = priv->audio_port[i].config;
 +              audio.format = AFMT_I2S;
 +              break;
 +      case HDMI_SPDIF:
 +              for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++)
 +                      if (priv->audio_port[i].format == AFMT_SPDIF)
 +                              audio.config = priv->audio_port[i].config;
 +              audio.format = AFMT_SPDIF;
 +              break;
 +      default:
 +              dev_err(dev, "%s: Invalid format %d\n", __func__, daifmt->fmt);
 +              return -EINVAL;
 +      }
 +
 +      if (audio.config == 0) {
 +              dev_err(dev, "%s: No audio configutation found\n", __func__);
 +              return -EINVAL;
 +      }
 +
 +      mutex_lock(&priv->audio_mutex);
 +      ret = tda998x_configure_audio(priv,
 +                                    &audio,
 +                                    priv->encoder.crtc->hwmode.clock);
 +
 +      if (ret == 0)
 +              priv->audio_params = audio;
 +      mutex_unlock(&priv->audio_mutex);
 +
 +      return ret;
 +}
 +
 +static void tda998x_audio_shutdown(struct device *dev, void *data)
 +{
 +      struct tda998x_priv *priv = dev_get_drvdata(dev);
 +
 +      mutex_lock(&priv->audio_mutex);
 +
 +      reg_write(priv, REG_ENA_AP, 0);
 +
 +      priv->audio_params.format = AFMT_UNUSED;
 +
 +      mutex_unlock(&priv->audio_mutex);
 +}
 +
 +static int
 +tda998x_audio_digital_mute(struct device *dev, void *data, bool enable)
 +{
 +      struct tda998x_priv *priv = dev_get_drvdata(dev);
 +
 +      mutex_lock(&priv->audio_mutex);
 +
 +      tda998x_audio_mute(priv, enable);
 +
 +      mutex_unlock(&priv->audio_mutex);
 +      return 0;
 +}
 +
 +static int tda998x_audio_get_eld(struct device *dev, void *data,
 +                               uint8_t *buf, size_t len)
 +{
 +      struct tda998x_priv *priv = dev_get_drvdata(dev);
 +      struct drm_mode_config *config = &priv->encoder.dev->mode_config;
 +      struct drm_connector *connector;
 +      int ret = -ENODEV;
 +
 +      mutex_lock(&config->mutex);
 +      list_for_each_entry(connector, &config->connector_list, head) {
 +              if (&priv->encoder == connector->encoder) {
 +                      memcpy(buf, connector->eld,
 +                             min(sizeof(connector->eld), len));
 +                      ret = 0;
 +              }
 +      }
 +      mutex_unlock(&config->mutex);
 +
 +      return ret;
 +}
 +
 +static const struct hdmi_codec_ops audio_codec_ops = {
 +      .hw_params = tda998x_audio_hw_params,
 +      .audio_shutdown = tda998x_audio_shutdown,
 +      .digital_mute = tda998x_audio_digital_mute,
 +      .get_eld = tda998x_audio_get_eld,
 +};
 +
 +static int tda998x_audio_codec_init(struct tda998x_priv *priv,
 +                                  struct device *dev)
 +{
 +      struct hdmi_codec_pdata codec_data = {
 +              .ops = &audio_codec_ops,
 +              .max_i2s_channels = 2,
 +      };
 +      int i;
 +
 +      for (i = 0; i < ARRAY_SIZE(priv->audio_port); i++) {
 +              if (priv->audio_port[i].format == AFMT_I2S &&
 +                  priv->audio_port[i].config != 0)
 +                      codec_data.i2s = 1;
 +              if (priv->audio_port[i].format == AFMT_SPDIF &&
 +                  priv->audio_port[i].config != 0)
 +                      codec_data.spdif = 1;
 +      }
 +
 +      priv->audio_pdev = platform_device_register_data(
 +              dev, HDMI_CODEC_DRV_NAME, PLATFORM_DEVID_AUTO,
 +              &codec_data, sizeof(codec_data));
 +
 +      return PTR_ERR_OR_ZERO(priv->audio_pdev);
 +}
 +
  /* I2C driver functions */
  
 +static int tda998x_get_audio_ports(struct tda998x_priv *priv,
 +                                 struct device_node *np)
 +{
 +      const u32 *port_data;
 +      u32 size;
 +      int i;
 +
 +      port_data = of_get_property(np, "audio-ports", &size);
 +      if (!port_data)
 +              return 0;
 +
 +      size /= sizeof(u32);
 +      if (size > 2 * ARRAY_SIZE(priv->audio_port) || size % 2 != 0) {
 +              dev_err(&priv->hdmi->dev,
 +                      "Bad number of elements in audio-ports dt-property\n");
 +              return -EINVAL;
 +      }
 +
 +      size /= 2;
 +
 +      for (i = 0; i < size; i++) {
 +              u8 afmt = be32_to_cpup(&port_data[2*i]);
 +              u8 ena_ap = be32_to_cpup(&port_data[2*i+1]);
 +
 +              if (afmt != AFMT_SPDIF && afmt != AFMT_I2S) {
 +                      dev_err(&priv->hdmi->dev,
 +                              "Bad audio format %u\n", afmt);
 +                      return -EINVAL;
 +              }
 +
 +              priv->audio_port[i].format = afmt;
 +              priv->audio_port[i].config = ena_ap;
 +      }
 +
 +      if (priv->audio_port[0].format == priv->audio_port[1].format) {
 +              dev_err(&priv->hdmi->dev,
 +                      "There can only be on I2S port and one SPDIF port\n");
 +              return -EINVAL;
 +      }
 +      return 0;
 +}
 +
  static int tda998x_create(struct i2c_client *client, struct tda998x_priv *priv)
  {
        struct device_node *np = client->dev.of_node;
        if (!np)
                return 0;               /* non-DT */
  
 -      /* get the optional video properties */
 +      /* 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_2 = video;
        }
  
 -      return 0;
 +      mutex_init(&priv->audio_mutex); /* Protect access from audio thread */
 +
 +      ret = tda998x_get_audio_ports(priv, np);
 +      if (ret)
 +              goto fail;
  
 +      if (priv->audio_port[0].format != AFMT_UNUSED)
 +              tda998x_audio_codec_init(priv, &client->dev);
 +
 +      return 0;
  fail:
        /* if encoder_init fails, the encoder slave is never registered,
         * so cleanup here:
@@@ -1585,7 -1369,6 +1585,6 @@@ const struct drm_connector_helper_func
  
  static void tda998x_connector_destroy(struct drm_connector *connector)
  {
-       drm_connector_unregister(connector);
        drm_connector_cleanup(connector);
  }
  
@@@ -1657,16 -1440,10 +1656,10 @@@ static int tda998x_bind(struct device *
        if (ret)
                goto err_connector;
  
-       ret = drm_connector_register(&priv->connector);
-       if (ret)
-               goto err_sysfs;
        drm_mode_connector_attach_encoder(&priv->connector, &priv->encoder);
  
        return 0;
  
- err_sysfs:
-       drm_connector_cleanup(&priv->connector);
  err_connector:
        drm_encoder_cleanup(&priv->encoder);
  err_encoder:
@@@ -1679,7 -1456,6 +1672,6 @@@ static void tda998x_unbind(struct devic
  {
        struct tda998x_priv *priv = dev_get_drvdata(dev);
  
-       drm_connector_unregister(&priv->connector);
        drm_connector_cleanup(&priv->connector);
        drm_encoder_cleanup(&priv->encoder);
        tda998x_destroy(priv);