[Freedreno] [PATCH] drm/msm/dp: unregister audio driver during unbind

2023-04-21 Thread Srinivas Kandagatla
while binding the code always registers a audio driver, however there
is no corresponding unregistration done in unbind. This leads to multiple
redundant audio platform devices if dp_display_bind and dp_display_unbind
happens multiple times during startup. On X13s platform this resulted in
6 to 9 audio codec device instead of just 3 codec devices for 3 dp ports.

Fix this by unregistering codecs on unbind.

Signed-off-by: Srinivas Kandagatla 
---
 drivers/gpu/drm/msm/dp/dp_audio.c   | 12 
 drivers/gpu/drm/msm/dp/dp_audio.h   |  2 ++
 drivers/gpu/drm/msm/dp/dp_display.c |  1 +
 3 files changed, 15 insertions(+)

diff --git a/drivers/gpu/drm/msm/dp/dp_audio.c 
b/drivers/gpu/drm/msm/dp/dp_audio.c
index 783e1468..1245c7aa49df 100644
--- a/drivers/gpu/drm/msm/dp/dp_audio.c
+++ b/drivers/gpu/drm/msm/dp/dp_audio.c
@@ -593,6 +593,18 @@ static struct hdmi_codec_pdata codec_data = {
.i2s = 1,
 };
 
+void dp_unregister_audio_driver(struct device *dev, struct dp_audio *dp_audio)
+{
+   struct dp_audio_private *audio_priv;
+
+   audio_priv = container_of(dp_audio, struct dp_audio_private, dp_audio);
+
+   if (audio_priv->audio_pdev) {
+   platform_device_unregister(audio_priv->audio_pdev);
+   audio_priv->audio_pdev = NULL;
+   }
+}
+
 int dp_register_audio_driver(struct device *dev,
struct dp_audio *dp_audio)
 {
diff --git a/drivers/gpu/drm/msm/dp/dp_audio.h 
b/drivers/gpu/drm/msm/dp/dp_audio.h
index 84e5f4a5d26b..4ab78880af82 100644
--- a/drivers/gpu/drm/msm/dp/dp_audio.h
+++ b/drivers/gpu/drm/msm/dp/dp_audio.h
@@ -53,6 +53,8 @@ struct dp_audio *dp_audio_get(struct platform_device *pdev,
 int dp_register_audio_driver(struct device *dev,
struct dp_audio *dp_audio);
 
+void dp_unregister_audio_driver(struct device *dev, struct dp_audio *dp_audio);
+
 /**
  * dp_audio_put()
  *
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c 
b/drivers/gpu/drm/msm/dp/dp_display.c
index 3e13acdfa7e5..99a38dbe51c0 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -326,6 +326,7 @@ static void dp_display_unbind(struct device *dev, struct 
device *master,
kthread_stop(dp->ev_tsk);
 
dp_power_client_deinit(dp->power);
+   dp_unregister_audio_driver(dev, dp->audio);
dp_aux_unregister(dp->aux);
dp->drm_dev = NULL;
dp->aux->drm_dev = NULL;
-- 
2.21.0



Re: [Freedreno] [PATCH 1/3] drm/msm: Fix speed-bin support not to access outside valid memory

2021-03-05 Thread Srinivas Kandagatla




On 05/03/2021 14:45, Doug Anderson wrote:

Hi,

On Fri, Mar 5, 2021 at 2:28 AM Srinivas Kandagatla
 wrote:




On 27/02/2021 00:26, Douglas Anderson wrote:

When running the latest kernel on an sc7180 with KASAN I got this
splat:
BUG: KASAN: slab-out-of-bounds in a6xx_gpu_init+0x618/0x644
Read of size 4 at addr ff8088f36100 by task kworker/7:1/58
CPU: 7 PID: 58 Comm: kworker/7:1 Not tainted 5.11.0+ #3
Hardware name: Google Lazor (rev1 - 2) with LTE (DT)
Workqueue: events deferred_probe_work_func
Call trace:
 dump_backtrace+0x0/0x3a8
 show_stack+0x24/0x30
 dump_stack+0x174/0x1e0
 print_address_description+0x70/0x2e4
 kasan_report+0x178/0x1bc
 __asan_report_load4_noabort+0x44/0x50
 a6xx_gpu_init+0x618/0x644
 adreno_bind+0x26c/0x438

This is because the speed bin is defined like this:
gpu_speed_bin: gpu_speed_bin@1d2 {
  reg = <0x1d2 0x2>;
  bits = <5 8>;
};

As you can see the "length" is 2 bytes. That means that the nvmem
subsystem allocates only 2 bytes. The GPU code, however, was casting
the pointer allocated by nvmem to a (u32 *) and dereferencing. That's
not so good.

Let's fix this to just use the nvmem_cell_read_u16() accessor function
which simplifies things and also gets rid of the splat.

Let's also put an explicit conversion from little endian in place just
to make things clear. The nvmem subsystem today is assuming little
endian and this makes it clear. Specifically, the way the above sc7180
cell is interpreted:

NVMEM:
   ++++++
   | .. | 0x1d3  | 0x1d2  | .. | 0x000  |
   ++++++
^   ^
   msb lsb

You can see that the least significant data is at the lower address
which is little endian.

NOTE: someone who is truly paying attention might wonder about me
picking the "u16" version of this accessor instead of the "u8" (since
the value is 8 bits big) or the u32 version (just for fun). At the
moment you need to pick the accessor that exactly matches the length
the cell was specified as in the device tree. Hopefully future
patches to the nvmem subsystem will fix this.

Fixes: fe7952c629da ("drm/msm: Add speed-bin support to a618 gpu")
Signed-off-by: Douglas Anderson 
---

   drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 31 +++
   1 file changed, 8 insertions(+), 23 deletions(-)

diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c 
b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
index ba8e9d3cf0fe..0e2024defd79 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
@@ -1350,35 +1350,20 @@ static int a6xx_set_supported_hw(struct device *dev, 
struct a6xx_gpu *a6xx_gpu,
   u32 revn)
   {
   struct opp_table *opp_table;
- struct nvmem_cell *cell;
   u32 supp_hw = UINT_MAX;
- void *buf;
-
- cell = nvmem_cell_get(dev, "speed_bin");
- /*
-  * -ENOENT means that the platform doesn't support speedbin which is
-  * fine
-  */
- if (PTR_ERR(cell) == -ENOENT)
- return 0;
- else if (IS_ERR(cell)) {
- DRM_DEV_ERROR(dev,
- "failed to read speed-bin. Some OPPs may not be 
supported by hardware");
- goto done;
- }
+ u16 speedbin;
+ int ret;

- buf = nvmem_cell_read(cell, NULL);


I think the issue here is not passing len pointer which should return
how many bytes the cell is!

Then from there we can decide to do le16_to_cpu or le32_to_cpu or not!
This will also future proof the code to handle speed_bins of different
sizes!


I think what you're saying is that you want to copy/paste this code
(or something similar) everywhere that accesses an nvmem cell.  Is
that correct?  ...or maybe you can suggest some smaller / shorter code
that I'm missing?



It depends what the consumer is doing! If it is already aware of what 
size of data its expecting then you can use nvmem_cell_read_u8/16/32/64 
variants, however it wants to do bit more with the data then 
nvmem_cell_read() should give more flexibility!



---

{
   struct nvmem_cell *cell;
   ssize_t len;
   char *ret;
   int i;

   *data = 0;

   cell = nvmem_cell_get(dev, cname);
   if (IS_ERR(cell)) {
 if (PTR_ERR(cell) != -EPROBE_DEFER)
   dev_err(dev, "undefined cell %s\n", cname);
 return PTR_ERR(cell);
   }

   ret = nvmem_cell_read(cell, &len);
   nvmem_cell_put(cell);
   if (IS_ERR(ret)) {
 dev_err(dev, "can't read cell %s\n", cname);
 return PTR_ERR(ret);
   }

   for (i = 0; i < len; i++)
 *data |= ret[i] << (8 * i);

   kfree(ret);
   dev_dbg(dev, "efuse read(%s) = %x, bytes %zd\n", cname, *data, len);

   return 0;
}

---

The above code is from cpr_read_efuse() in "cpr.c".  I mentioned in
the c

Re: [Freedreno] [PATCH 1/3] drm/msm: Fix speed-bin support not to access outside valid memory

2021-03-05 Thread Srinivas Kandagatla




On 27/02/2021 00:26, Douglas Anderson wrote:

When running the latest kernel on an sc7180 with KASAN I got this
splat:
   BUG: KASAN: slab-out-of-bounds in a6xx_gpu_init+0x618/0x644
   Read of size 4 at addr ff8088f36100 by task kworker/7:1/58
   CPU: 7 PID: 58 Comm: kworker/7:1 Not tainted 5.11.0+ #3
   Hardware name: Google Lazor (rev1 - 2) with LTE (DT)
   Workqueue: events deferred_probe_work_func
   Call trace:
dump_backtrace+0x0/0x3a8
show_stack+0x24/0x30
dump_stack+0x174/0x1e0
print_address_description+0x70/0x2e4
kasan_report+0x178/0x1bc
__asan_report_load4_noabort+0x44/0x50
a6xx_gpu_init+0x618/0x644
adreno_bind+0x26c/0x438

This is because the speed bin is defined like this:
   gpu_speed_bin: gpu_speed_bin@1d2 {
 reg = <0x1d2 0x2>;
 bits = <5 8>;
   };

As you can see the "length" is 2 bytes. That means that the nvmem
subsystem allocates only 2 bytes. The GPU code, however, was casting
the pointer allocated by nvmem to a (u32 *) and dereferencing. That's
not so good.

Let's fix this to just use the nvmem_cell_read_u16() accessor function
which simplifies things and also gets rid of the splat.

Let's also put an explicit conversion from little endian in place just
to make things clear. The nvmem subsystem today is assuming little
endian and this makes it clear. Specifically, the way the above sc7180
cell is interpreted:

NVMEM:
  ++++++
  | .. | 0x1d3  | 0x1d2  | .. | 0x000  |
  ++++++
   ^   ^
  msb lsb

You can see that the least significant data is at the lower address
which is little endian.

NOTE: someone who is truly paying attention might wonder about me
picking the "u16" version of this accessor instead of the "u8" (since
the value is 8 bits big) or the u32 version (just for fun). At the
moment you need to pick the accessor that exactly matches the length
the cell was specified as in the device tree. Hopefully future
patches to the nvmem subsystem will fix this.

Fixes: fe7952c629da ("drm/msm: Add speed-bin support to a618 gpu")
Signed-off-by: Douglas Anderson 
---

  drivers/gpu/drm/msm/adreno/a6xx_gpu.c | 31 +++
  1 file changed, 8 insertions(+), 23 deletions(-)

diff --git a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c 
b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
index ba8e9d3cf0fe..0e2024defd79 100644
--- a/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
+++ b/drivers/gpu/drm/msm/adreno/a6xx_gpu.c
@@ -1350,35 +1350,20 @@ static int a6xx_set_supported_hw(struct device *dev, 
struct a6xx_gpu *a6xx_gpu,
u32 revn)
  {
struct opp_table *opp_table;
-   struct nvmem_cell *cell;
u32 supp_hw = UINT_MAX;
-   void *buf;
-
-   cell = nvmem_cell_get(dev, "speed_bin");
-   /*
-* -ENOENT means that the platform doesn't support speedbin which is
-* fine
-*/
-   if (PTR_ERR(cell) == -ENOENT)
-   return 0;
-   else if (IS_ERR(cell)) {
-   DRM_DEV_ERROR(dev,
-   "failed to read speed-bin. Some OPPs may not be 
supported by hardware");
-   goto done;
-   }
+   u16 speedbin;
+   int ret;
  
-	buf = nvmem_cell_read(cell, NULL);


I think the issue here is not passing len pointer which should return 
how many bytes the cell is!


Then from there we can decide to do le16_to_cpu or le32_to_cpu or not!
This will also future proof the code to handle speed_bins of different 
sizes!


--srini


-   if (IS_ERR(buf)) {
-   nvmem_cell_put(cell);



+   ret = nvmem_cell_read_u16(dev, "speed_bin", &speedbin);
+   if (ret) {
DRM_DEV_ERROR(dev,
-   "failed to read speed-bin. Some OPPs may not be 
supported by hardware");
+ "failed to read speed-bin (%d). Some OPPs may not be 
supported by hardware",
+ ret);
goto done;
}
+   speedbin = le16_to_cpu(speedbin);
  
-	supp_hw = fuse_to_supp_hw(dev, revn, *((u32 *) buf));

-
-   kfree(buf);
-   nvmem_cell_put(cell);
+   supp_hw = fuse_to_supp_hw(dev, revn, speedbin);
  
  done:

opp_table = dev_pm_opp_set_supported_hw(dev, &supp_hw, 1);


___
Freedreno mailing list
Freedreno@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/freedreno


Re: [Freedreno] [PATCH v14 0/4] iommu/arm-smmu: Add runtime pm/sleep support

2018-08-21 Thread Srinivas Kandagatla



On 27/07/18 08:02, Vivek Gautam wrote:

Sricharan R (3):
   iommu/arm-smmu: Add pm_runtime/sleep ops
   iommu/arm-smmu: Invoke pm_runtime during probe, add/remove device
   iommu/arm-smmu: Add the device_link between masters and smmu

Vivek Gautam (1):
   iommu/arm-smmu: Add support for qcom,smmu-v2 variant

  .../devicetree/bindings/iommu/arm,smmu.txt |  42 +
  drivers/iommu/arm-smmu.c   | 194 +++--
  2 files changed, 225 insertions(+), 11 deletions(-)
MSM8896 audio and display on top of mainline totally depends on this 
patches, I have been testing various version this series on DB820c for 
both display and audio.


Tested-by: Srinivas Kandagatla 


--srini
___
Freedreno mailing list
Freedreno@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/freedreno


[Freedreno] [PATCH v2] drm: msm: Add ASoC generic hdmi audio codec support.

2016-06-10 Thread Srinivas Kandagatla
This patch adds support to generic audio codec via
ASoC hdmi-codec infrastucture which is merged recently.

Signed-off-by: Srinivas Kandagatla 
---
Changes since v1:
-Fixed typo for 44.1Kz case spotted by Jyri Sarha

 drivers/gpu/drm/msm/Kconfig |   1 +
 drivers/gpu/drm/msm/hdmi/hdmi.c | 120 +++-
 drivers/gpu/drm/msm/hdmi/hdmi.h |  14 +
 3 files changed, 134 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index 167a497..7c7a031 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -10,6 +10,7 @@ config DRM_MSM
select SHMEM
select TMPFS
select QCOM_SCM
+   select SND_SOC_HDMI_CODEC if SND_SOC
default y
help
  DRM/KMS driver for MSM/snapdragon.
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c
index 51b9ea5..aada355 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.c
@@ -19,6 +19,7 @@
 #include 
 #include 
 
+#include 
 #include "hdmi.h"
 
 void msm_hdmi_set_mode(struct hdmi *hdmi, bool power_on)
@@ -434,6 +435,114 @@ static int msm_hdmi_get_gpio(struct device_node *of_node, 
const char *name)
return gpio;
 }
 
+/*
+ * HDMI audio codec callbacks
+ */
+static int msm_hdmi_audio_hw_params(struct device *dev,
+   struct hdmi_codec_daifmt *daifmt,
+   struct hdmi_codec_params *params)
+{
+   struct hdmi *hdmi = dev_get_drvdata(dev);
+   unsigned int chan;
+   unsigned int channel_allocation = 0;
+   unsigned int rate;
+   unsigned int level_shift  = 0; /* 0dB */
+   bool down_mix = false;
+
+   dev_dbg(dev, "%u Hz, %d bit, %d channels\n", params->sample_rate,
+params->sample_width, params->cea.channels);
+
+   switch (params->cea.channels) {
+   case 2:
+   /* FR and FL speakers */
+   channel_allocation  = 0;
+   chan = MSM_HDMI_AUDIO_CHANNEL_2;
+   break;
+   case 4:
+   /* FC, LFE, FR and FL speakers */
+   channel_allocation  = 0x3;
+   chan = MSM_HDMI_AUDIO_CHANNEL_4;
+   break;
+   case 6:
+   /* RR, RL, FC, LFE, FR and FL speakers */
+   channel_allocation  = 0x0B;
+   chan = MSM_HDMI_AUDIO_CHANNEL_6;
+   break;
+   case 8:
+   /* FRC, FLC, RR, RL, FC, LFE, FR and FL speakers */
+   channel_allocation  = 0x1F;
+   chan = MSM_HDMI_AUDIO_CHANNEL_8;
+   break;
+   default:
+   return -EINVAL;
+   }
+
+   switch (params->sample_rate) {
+   case 32000:
+   rate = HDMI_SAMPLE_RATE_32KHZ;
+   break;
+   case 44100:
+   rate = HDMI_SAMPLE_RATE_44_1KHZ;
+   break;
+   case 48000:
+   rate = HDMI_SAMPLE_RATE_48KHZ;
+   break;
+   case 88200:
+   rate = HDMI_SAMPLE_RATE_88_2KHZ;
+   break;
+   case 96000:
+   rate = HDMI_SAMPLE_RATE_96KHZ;
+   break;
+   case 176400:
+   rate = HDMI_SAMPLE_RATE_176_4KHZ;
+   break;
+   case 192000:
+   rate = HDMI_SAMPLE_RATE_192KHZ;
+   break;
+   default:
+   dev_err(dev, "rate[%d] not supported!\n",
+   params->sample_rate);
+   return -EINVAL;
+   }
+
+   msm_hdmi_audio_set_sample_rate(hdmi, rate);
+   msm_hdmi_audio_info_setup(hdmi, 1, chan, channel_allocation,
+ level_shift, down_mix);
+
+   return 0;
+}
+
+static void msm_hdmi_audio_shutdown(struct device *dev)
+{
+   struct hdmi *hdmi = dev_get_drvdata(dev);
+
+   msm_hdmi_audio_info_setup(hdmi, 0, 0, 0, 0, 0);
+}
+
+static const struct hdmi_codec_ops msm_hdmi_audio_codec_ops = {
+   .hw_params = msm_hdmi_audio_hw_params,
+   .audio_shutdown = msm_hdmi_audio_shutdown,
+};
+
+static struct hdmi_codec_pdata codec_data = {
+   .ops = &msm_hdmi_audio_codec_ops,
+   .max_i2s_channels = 8,
+   .i2s = 1,
+};
+
+static int msm_hdmi_register_audio_driver(struct hdmi *hdmi, struct device 
*dev)
+{
+   hdmi->audio_pdev = platform_device_register_data(dev,
+HDMI_CODEC_DRV_NAME,
+PLATFORM_DEVID_AUTO,
+&codec_data,
+sizeof(codec_data));
+   if (IS_ERR(hdmi->audio_pdev))
+   return PTR_ERR(hdmi->audio_pdev);
+
+   return 0;
+}
+
 static int msm_hdmi_bind(struct device *dev, struct device *master, void *data)
 {
struct drm_devi

Re: [Freedreno] [RFC PATCH] drm: msm: Add ASoC generic hdmi audio codec support.

2016-06-06 Thread Srinivas Kandagatla



On 06/06/16 13:23, Jyri Sarha wrote:

On 06/03/16 16:56, Srinivas Kandagatla wrote:

>This patch adds support to generic audio codec via
>ASoC hdmi-codec infrastucture which is merged recently.
>

I know nothing about msm HW, but from the hdmi-codec point of view this
looks like a correct usage. However, the hdmi-codec could probably do
more to connect the hdmi audio infoframe's channel allocation field and
ALSA's channel mapping API together.


Yes, that would be nice to get the channel allocation into hdmi-codec.



There looks to be a bug in selecting 44100 sample rate bellow.



Thanks for spotting this, yes this looks like a typo, I will fix this in v2.

Thanks,
srini


BR,
Jyri


>Signed-off-by: Srinivas Kandagatla
>---
>  drivers/gpu/drm/msm/Kconfig |   1 +
>  drivers/gpu/drm/msm/hdmi/hdmi.c | 120 
+++-
>  drivers/gpu/drm/msm/hdmi/hdmi.h |  14 +
>  3 files changed, 134 insertions(+), 1 deletion(-)

>diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c
>index 51b9ea5..3281496 100644
>--- a/drivers/gpu/drm/msm/hdmi/hdmi.c
>+++ b/drivers/gpu/drm/msm/hdmi/hdmi.c
>@@ -19,6 +19,7 @@
>  #include 
>  #include 
>
>+#include 
>  #include "hdmi.h"
>
>  void msm_hdmi_set_mode(struct hdmi *hdmi, bool power_on)
>@@ -434,6 +435,114 @@ static int msm_hdmi_get_gpio(struct device_node 
*of_node, const char *name)
>return gpio;

...

>+   case 32000:
>+   rate = HDMI_SAMPLE_RATE_32KHZ;
>+   break;
>+   case 44100:
>+   rate = HDMI_SAMPLE_RATE_48KHZ;
>+   break;

This looks like a bug...


___
Freedreno mailing list
Freedreno@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/freedreno


[Freedreno] [RFC PATCH] drm: msm: Add ASoC generic hdmi audio codec support.

2016-06-03 Thread Srinivas Kandagatla
This patch adds support to generic audio codec via
ASoC hdmi-codec infrastucture which is merged recently.

Signed-off-by: Srinivas Kandagatla 
---
 drivers/gpu/drm/msm/Kconfig |   1 +
 drivers/gpu/drm/msm/hdmi/hdmi.c | 120 +++-
 drivers/gpu/drm/msm/hdmi/hdmi.h |  14 +
 3 files changed, 134 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/msm/Kconfig b/drivers/gpu/drm/msm/Kconfig
index 167a497..7c7a031 100644
--- a/drivers/gpu/drm/msm/Kconfig
+++ b/drivers/gpu/drm/msm/Kconfig
@@ -10,6 +10,7 @@ config DRM_MSM
select SHMEM
select TMPFS
select QCOM_SCM
+   select SND_SOC_HDMI_CODEC if SND_SOC
default y
help
  DRM/KMS driver for MSM/snapdragon.
diff --git a/drivers/gpu/drm/msm/hdmi/hdmi.c b/drivers/gpu/drm/msm/hdmi/hdmi.c
index 51b9ea5..3281496 100644
--- a/drivers/gpu/drm/msm/hdmi/hdmi.c
+++ b/drivers/gpu/drm/msm/hdmi/hdmi.c
@@ -19,6 +19,7 @@
 #include 
 #include 
 
+#include 
 #include "hdmi.h"
 
 void msm_hdmi_set_mode(struct hdmi *hdmi, bool power_on)
@@ -434,6 +435,114 @@ static int msm_hdmi_get_gpio(struct device_node *of_node, 
const char *name)
return gpio;
 }
 
+/*
+ * HDMI audio codec callbacks
+ */
+static int msm_hdmi_audio_hw_params(struct device *dev,
+   struct hdmi_codec_daifmt *daifmt,
+   struct hdmi_codec_params *params)
+{
+   struct hdmi *hdmi = dev_get_drvdata(dev);
+   unsigned int chan;
+   unsigned int channel_allocation = 0;
+   unsigned int rate;
+   unsigned int level_shift  = 0; /* 0dB */
+   bool down_mix = false;
+
+   dev_dbg(dev, "%u Hz, %d bit, %d channels\n", params->sample_rate,
+params->sample_width, params->cea.channels);
+
+   switch (params->cea.channels) {
+   case 2:
+   /* FR and FL speakers */
+   channel_allocation  = 0;
+   chan = MSM_HDMI_AUDIO_CHANNEL_2;
+   break;
+   case 4:
+   /* FC, LFE, FR and FL speakers */
+   channel_allocation  = 0x3;
+   chan = MSM_HDMI_AUDIO_CHANNEL_4;
+   break;
+   case 6:
+   /* RR, RL, FC, LFE, FR and FL speakers */
+   channel_allocation  = 0x0B;
+   chan = MSM_HDMI_AUDIO_CHANNEL_6;
+   break;
+   case 8:
+   /* FRC, FLC, RR, RL, FC, LFE, FR and FL speakers */
+   channel_allocation  = 0x1F;
+   chan = MSM_HDMI_AUDIO_CHANNEL_8;
+   break;
+   default:
+   return -EINVAL;
+   }
+
+   switch (params->sample_rate) {
+   case 32000:
+   rate = HDMI_SAMPLE_RATE_32KHZ;
+   break;
+   case 44100:
+   rate = HDMI_SAMPLE_RATE_48KHZ;
+   break;
+   case 48000:
+   rate = HDMI_SAMPLE_RATE_48KHZ;
+   break;
+   case 88200:
+   rate = HDMI_SAMPLE_RATE_88_2KHZ;
+   break;
+   case 96000:
+   rate = HDMI_SAMPLE_RATE_96KHZ;
+   break;
+   case 176400:
+   rate = HDMI_SAMPLE_RATE_176_4KHZ;
+   break;
+   case 192000:
+   rate = HDMI_SAMPLE_RATE_192KHZ;
+   break;
+   default:
+   dev_err(dev, "rate[%d] not supported!\n",
+   params->sample_rate);
+   return -EINVAL;
+   }
+
+   msm_hdmi_audio_set_sample_rate(hdmi, rate);
+   msm_hdmi_audio_info_setup(hdmi, 1, chan, channel_allocation,
+ level_shift, down_mix);
+
+   return 0;
+}
+
+static void msm_hdmi_audio_shutdown(struct device *dev)
+{
+   struct hdmi *hdmi = dev_get_drvdata(dev);
+
+   msm_hdmi_audio_info_setup(hdmi, 0, 0, 0, 0, 0);
+}
+
+static const struct hdmi_codec_ops msm_hdmi_audio_codec_ops = {
+   .hw_params = msm_hdmi_audio_hw_params,
+   .audio_shutdown = msm_hdmi_audio_shutdown,
+};
+
+static struct hdmi_codec_pdata codec_data = {
+   .ops = &msm_hdmi_audio_codec_ops,
+   .max_i2s_channels = 8,
+   .i2s = 1,
+};
+
+static int msm_hdmi_register_audio_driver(struct hdmi *hdmi, struct device 
*dev)
+{
+   hdmi->audio_pdev = platform_device_register_data(dev,
+HDMI_CODEC_DRV_NAME,
+PLATFORM_DEVID_AUTO,
+&codec_data,
+sizeof(codec_data));
+   if (IS_ERR(hdmi->audio_pdev))
+   return PTR_ERR(hdmi->audio_pdev);
+
+   return 0;
+}
+
 static int msm_hdmi_bind(struct device *dev, struct device *master, void *data)
 {
struct drm_device *drm = dev_get_drvdata(master);
@@ -441,7 +550,7 @@ static int msm_hdmi_bind(struc