From 310a271a0989ea0a231d86a57bd0d3900ec832bc Mon Sep 17 00:00:00 2001 From: Shengjiu Wang Date: Tue, 22 May 2018 15:32:51 +0800 Subject: [PATCH] MLK-18368-4: hdmi_rx: add generic hdmi audio rx driver Register generic hdmi audio rx driver. Signed-off-by: Shengjiu Wang --- drivers/media/platform/imx8/hdmi/Makefile | 3 +- .../platform/imx8/hdmi/mxc-hdmi-rx-audio.c | 251 ++++++++++++++++++ .../media/platform/imx8/hdmi/mxc-hdmi-rx.c | 2 + .../media/platform/imx8/hdmi/mxc-hdmi-rx.h | 4 + 4 files changed, 259 insertions(+), 1 deletion(-) create mode 100644 drivers/media/platform/imx8/hdmi/mxc-hdmi-rx-audio.c diff --git a/drivers/media/platform/imx8/hdmi/Makefile b/drivers/media/platform/imx8/hdmi/Makefile index 9a466a52bc80..93c0b3f44d72 100644 --- a/drivers/media/platform/imx8/hdmi/Makefile +++ b/drivers/media/platform/imx8/hdmi/Makefile @@ -1 +1,2 @@ -obj-$(CONFIG_IMX8_HDMI_RX) += mxc-hdmi-rx.o mxc-hdmi-hw.o API_AFE_ss28fdsoi_hdmirx.o +obj-$(CONFIG_IMX8_HDMI_RX) += mxc-hdmi-rx.o mxc-hdmi-hw.o API_AFE_ss28fdsoi_hdmirx.o \ + mxc-hdmi-rx-audio.o diff --git a/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx-audio.c b/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx-audio.c new file mode 100644 index 000000000000..d7d9a5ebdd58 --- /dev/null +++ b/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx-audio.c @@ -0,0 +1,251 @@ +/* + * Copyright 2018 NXP + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mxc-hdmi-rx.h" +#include "../../../../mxc/hdp/API_Audio.h" +#include "../../../../mxc/hdp/API_HDMI_RX_Audio.h" +#include "../../../../mxc/hdp/sink_pif.h" +#include "../../../../mxc/hdp/aif_pckt2smp.h" + + +static int get_audio_infoframe(state_struct *state, unsigned int *chan) +{ + + unsigned int regread; + int ret = 0; + int times = 0; + + cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_TYPE_CFG1 << 2), F_INFO_TYPE1(0x84)); + + cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INT_MASK << 2), 0x1FFFE); + + do { + cdn_apb_read(state, ADDR_SINK_PIF + (PKT_INT_STATUS << 2), ®read); + udelay(10); + times++; + } while (!(regread & (1 << 0)) && times < 100); + + + if (times == 100) { + ret = -EINVAL; + return ret; + } + + cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_CTRL << 2), F_PACKET_RDN_WR(0x0) | F_PACKET_NUM(0x0)); + + times = 0; + + do { + cdn_apb_read(state, ADDR_SINK_PIF + (PKT_INT_STATUS << 2), ®read); + udelay(10); + times++; + } while (!(regread & (1 << 16)) && times < 100); + + if (times == 100) { + ret = -EINVAL; + return ret; + } + + cdn_apb_read(state, ADDR_SINK_PIF + (PKT_INFO_DATA1 << 2), ®read); + + cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_TYPE_CFG1 << 2), F_INFO_TYPE1(0x00)); + + *chan = ((regread & 0x700) >> 8) + 1; + + cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_HEADER << 2), 0); + cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_DATA1 << 2), 0); + cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_DATA2 << 2), 0); + cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_DATA3 << 2), 0); + cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_DATA4 << 2), 0); + cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_DATA5 << 2), 0); + cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_DATA6 << 2), 0); + cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_DATA7 << 2), 0); + + cdn_apb_write(state, ADDR_SINK_PIF + (PKT_INFO_CTRL << 2), F_PACKET_RDN_WR(0x1) | F_PACKET_NUM(0x0)); + + times = 0; + do { + cdn_apb_read(state, ADDR_SINK_PIF + (PKT_INT_STATUS << 2), ®read); + udelay(10); + times++; + } while (!(regread & (1 << 16)) && times < 100); + + if (times == 100) { + ret = -EINVAL; + return ret; + } + + return ret; +} + +static u32 TMDS_rate_table[7] = { +25200, 27000, 54000, 74250, 148500, 297000, 594000, +}; + +static u32 N_table_32k[8] = { +/*25200, 27000, 54000, 74250, 148500, 297000, 594000,*/ +4096, 4096, 4096, 4096, 4096, 3072, 3072, 4096, +}; + +static u32 N_table_44k[8] = { +6272, 6272, 6272, 6272, 6272, 4704, 9408, 6272, +}; + +static u32 N_table_48k[8] = { +6144, 6144, 6144, 6144, 6144, 5120, 6144, 6144, +}; + +static int select_rate(u32 pclk, u32 N) +{ + int i = 0; + int rate = 0; + + for (i = 0; i < 7; i++) { + if (pclk == TMDS_rate_table[i]) + break; + } + + if (i == 7) + DRM_WARN("pclkc %d is not supported!\n", pclk); + + if (N_table_32k[i] == N) + rate = 32000; + + if (N_table_44k[i] == N) + rate = 44100; + + if (N_table_44k[i] * 2 == N) + rate = 44100 * 2; + + if (N_table_44k[i] * 4 == N) + rate = 44100 * 4; + + if (N_table_48k[i] == N) + rate = 48000; + + if (N_table_48k[i] * 2 == N) + rate = 48000 * 2; + + if (N_table_48k[i] * 4 == N) + rate = 48000 * 4; + + return rate; +} + + + +static int mxc_hdmi_rx_audio(struct mxc_hdmi_rx_dev *hdmi) +{ + state_struct *state = &hdmi->state; + u32 regread; + u32 rate; + u32 chan = 2; + CDN_API_STATUS status; + int ret; + + ret = get_audio_infoframe(state, &chan); + if (ret) + return ret; + + status = CDN_API_RX_AudioAutoConfig(state, chan, chan/2, 0, 32, 32); + if (status != CDN_OK) + return -EINVAL; + + if (cdn_apb_read(state, ADDR_AIF_ENCODER + (AIF_ACR_N_ST << 2), ®read)) + return -EINVAL; + + rate = select_rate(hdmi->timings->timings.bt.pixelclock/1000, regread); + + hdmi->channels = chan; + hdmi->sample_rate = rate; + + return 0; +} + +/* + * HDMI audio codec callbacks + */ +static int mxc_hdmi_rx_audio_hw_params(struct device *dev, void *data, + struct hdmi_codec_daifmt *daifmt, + struct hdmi_codec_params *params) +{ + return 0; +} + +static void mxc_hdmi_rx_audio_shutdown(struct device *dev, void *data) +{ + pm_runtime_put_sync(dev); +} + +static int mxc_hdmi_rx_audio_startup(struct device *dev, void *data) +{ + struct mxc_hdmi_rx_dev *hdmi = dev_get_drvdata(dev); + int ret; + + pm_runtime_get_sync(dev); + ret = mxc_hdmi_rx_audio(hdmi); + + return ret; +} + +static int mxc_hdmi_rx_audio_get_eld(struct device *dev, void *data, uint8_t *buf, size_t len) +{ + struct mxc_hdmi_rx_dev *hdmi = dev_get_drvdata(dev); + + if (len < 8) + return -EINVAL; + + memcpy(buf, &hdmi->sample_rate, 4); + memcpy(buf + 4, &hdmi->channels, 4); + + return 0; +} + +static const struct hdmi_codec_ops mxc_hdmi_rx_audio_codec_ops = { + .hw_params = mxc_hdmi_rx_audio_hw_params, + .audio_shutdown = mxc_hdmi_rx_audio_shutdown, + .audio_startup = mxc_hdmi_rx_audio_startup, + .get_eld = mxc_hdmi_rx_audio_get_eld, +}; + +void mxc_hdmi_rx_register_audio_driver(struct device *dev) +{ + struct hdmi_codec_pdata codec_data = { + .ops = &mxc_hdmi_rx_audio_codec_ops, + .max_i2s_channels = 8, + .i2s = 1, + }; + struct platform_device *pdev; + + pdev = platform_device_register_data(dev, HDMI_CODEC_DRV_NAME, + 2, &codec_data, + sizeof(codec_data)); + if (IS_ERR(pdev)) + return; + + DRM_INFO("%s driver bound to HDMI\n", HDMI_CODEC_DRV_NAME); +} + + diff --git a/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.c b/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.c index 2ec13660c5cb..0ab1308dc629 100644 --- a/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.c +++ b/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.c @@ -777,6 +777,8 @@ static int mxc_hdmi_probe(struct platform_device *pdev) } #endif + mxc_hdmi_rx_register_audio_driver(dev); + dev_info(dev, "mxc hdmi rx probe successfully\n"); return ret; diff --git a/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.h b/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.h index 777d6238190d..85baaffeeab4 100644 --- a/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.h +++ b/drivers/media/platform/imx8/hdmi/mxc-hdmi-rx.h @@ -118,6 +118,9 @@ struct mxc_hdmi_rx_dev { u8 is_cec; struct imx_cec_dev cec; + u32 sample_rate; + u32 sample_width; + u32 channels; }; enum mxc_hdmi_rx_power_state { @@ -130,5 +133,6 @@ int hdmirx_startup(state_struct *state); void imx8qm_hdmi_phy_reset(state_struct *state, u8 reset); int hdmi_rx_init(state_struct *state); int mxc_hdmi_frame_timing(struct mxc_hdmi_rx_dev *hdmi_rx); +void mxc_hdmi_rx_register_audio_driver(struct device *dev); #endif -- 2.17.1