Changes in 4.9.180 ext4: do not delete unlinked inode from orphan list on failed truncate KVM: x86: fix return value for reserved EFER bio: fix improper use of smp_mb__before_atomic() Revert "scsi: sd: Keep disk read-only when re-reading partition" crypto: vmx - CTR: always increment IV as quadword kvm: svm/avic: fix off-by-one in checking host APIC ID libnvdimm/namespace: Fix label tracking error arm64: Save and restore OSDLR_EL1 across suspend/resume gfs2: Fix sign extension bug in gfs2_update_stats Btrfs: do not abort transaction at btrfs_update_root() after failure to COW path Btrfs: fix race between ranged fsync and writeback of adjacent ranges btrfs: sysfs: don't leak memory when failing add fsid fbdev: fix divide error in fb_var_to_videomode hugetlb: use same fault hash key for shared and private mappings fbdev: fix WARNING in __alloc_pages_nodemask bug media: cpia2: Fix use-after-free in cpia2_exit media: vivid: use vfree() instead of kfree() for dev->bitmap_cap ssb: Fix possible NULL pointer dereference in ssb_host_pcmcia_exit at76c50x-usb: Don't register led_trigger if usb_register_driver failed perf tools: No need to include bitops.h in util.h tools include: Adopt linux/bits.h Revert "btrfs: Honour FITRIM range constraints during free space trim" gfs2: Fix lru_count going negative cxgb4: Fix error path in cxgb4_init_module mmc: core: Verify SD bus width dmaengine: tegra210-dma: free dma controller in remove() net: ena: gcc 8: fix compilation warning ASoC: hdmi-codec: unlock the device on startup errors powerpc/boot: Fix missing check of lseek() return value ASoC: imx: fix fiq dependencies spi: pxa2xx: fix SCR (divisor) calculation brcm80211: potential NULL dereference in brcmf_cfg80211_vndr_cmds_dcmd_handler() ARM: vdso: Remove dependency with the arch_timer driver internals arm64: Fix compiler warning from pte_unmap() with -Wunused-but-set-variable sched/cpufreq: Fix kobject memleak scsi: qla2xxx: Fix a qla24xx_enable_msix() error path iwlwifi: pcie: don't crash on invalid RX interrupt rtc: 88pm860x: prevent use-after-free on device remove w1: fix the resume command API dmaengine: pl330: _stop: clear interrupt status mac80211/cfg80211: update bss channel on channel switch ASoC: fsl_sai: Update is_slave_mode with correct value mwifiex: prevent an array overflow net: cw1200: fix a NULL pointer dereference crypto: sun4i-ss - Fix invalid calculation of hash end bcache: return error immediately in bch_journal_replay() bcache: fix failure in journal relplay bcache: add failure check to run_cache_set() for journal replay bcache: avoid clang -Wunintialized warning x86/build: Move _etext to actual end of .text smpboot: Place the __percpu annotation correctly x86/mm: Remove in_nmi() warning from 64-bit implementation of vmalloc_fault() mm/uaccess: Use 'unsigned long' to placate UBSAN warnings on older GCC versions HID: logitech-hidpp: use RAP instead of FAP to get the protocol version pinctrl: pistachio: fix leaked of_node references dmaengine: at_xdmac: remove BUG_ON macro in tasklet media: coda: clear error return value before picture run media: ov6650: Move v4l2_clk_get() to ov6650_video_probe() helper media: au0828: stop video streaming only when last user stops media: ov2659: make S_FMT succeed even if requested format doesn't match audit: fix a memory leak bug media: au0828: Fix NULL pointer dereference in au0828_analog_stream_enable() media: pvrusb2: Prevent a buffer overflow powerpc/numa: improve control of topology updates sched/core: Check quota and period overflow at usec to nsec conversion sched/core: Handle overflow in cpu_shares_write_u64 USB: core: Don't unbind interfaces following device reset failure x86/irq/64: Limit IST stack overflow check to #DB stack i40e: don't allow changes to HW VLAN stripping on active port VLANs arm64: vdso: Fix clock_getres() for CLOCK_REALTIME RDMA/cxgb4: Fix null pointer dereference on alloc_skb failure hwmon: (vt1211) Use request_muxed_region for Super-IO accesses hwmon: (smsc47m1) Use request_muxed_region for Super-IO accesses hwmon: (smsc47b397) Use request_muxed_region for Super-IO accesses hwmon: (pc87427) Use request_muxed_region for Super-IO accesses hwmon: (f71805f) Use request_muxed_region for Super-IO accesses scsi: libsas: Do discovery on empty PHY to update PHY info mmc: core: make pwrseq_emmc (partially) support sleepy GPIO controllers mmc_spi: add a status check for spi_sync_locked mmc: sdhci-of-esdhc: add erratum eSDHC5 support mmc: sdhci-of-esdhc: add erratum eSDHC-A001 and A-008358 support PM / core: Propagate dev->power.wakeup_path when no callbacks extcon: arizona: Disable mic detect if running when driver is removed s390: cio: fix cio_irb declaration cpufreq: ppc_cbe: fix possible object reference leak cpufreq/pasemi: fix possible object reference leak cpufreq: pmac32: fix possible object reference leak x86/build: Keep local relocations with ld.lld iio: ad_sigma_delta: Properly handle SPI bus locking vs CS assertion iio: hmc5843: fix potential NULL pointer dereferences iio: common: ssp_sensors: Initialize calculated_time in ssp_common_process_data rtlwifi: fix a potential NULL pointer dereference mwifiex: Fix mem leak in mwifiex_tm_cmd brcmfmac: fix missing checks for kmemdup b43: shut up clang -Wuninitialized variable warning brcmfmac: convert dev_init_lock mutex to completion brcmfmac: fix race during disconnect when USB completion is in progress brcmfmac: fix Oops when bringing up interface during USB disconnect scsi: ufs: Fix regulator load and icc-level configuration scsi: ufs: Avoid configuring regulator with undefined voltage range arm64: cpu_ops: fix a leaked reference by adding missing of_node_put x86/uaccess, signal: Fix AC=1 bloat x86/ia32: Fix ia32_restore_sigcontext() AC leak chardev: add additional check for minor range overlap HID: core: move Usage Page concatenation to Main item ASoC: eukrea-tlv320: fix a leaked reference by adding missing of_node_put ASoC: fsl_utils: fix a leaked reference by adding missing of_node_put cxgb3/l2t: Fix undefined behaviour spi: tegra114: reset controller on probe media: wl128x: prevent two potential buffer overflows virtio_console: initialize vtermno value for ports tty: ipwireless: fix missing checks for ioremap x86/mce: Fix machine_check_poll() tests for error types rcutorture: Fix cleanup path for invalid torture_type strings rcuperf: Fix cleanup path for invalid perf_type strings usb: core: Add PM runtime calls to usb_hcd_platform_shutdown scsi: qla4xxx: avoid freeing unallocated dma memory dmaengine: tegra210-adma: use devm_clk_*() helpers media: m88ds3103: serialize reset messages in m88ds3103_set_frontend media: go7007: avoid clang frame overflow warning with KASAN scsi: lpfc: Fix FDMI manufacturer attribute value media: saa7146: avoid high stack usage with clang scsi: lpfc: Fix SLI3 commands being issued on SLI4 devices spi : spi-topcliff-pch: Fix to handle empty DMA buffers spi: rspi: Fix sequencer reset during initialization spi: Fix zero length xfer bug ASoC: davinci-mcasp: Fix clang warning without CONFIG_PM drm: Wake up next in drm_read() chain if we are forced to putback the event Linux 4.9.180 Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
504 lines
12 KiB
C
504 lines
12 KiB
C
/*
|
|
* ALSA SoC codec for HDMI encoder drivers
|
|
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
|
|
* Author: Jyri Sarha <jsarha@ti.com>
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* version 2 as published by the Free Software Foundation.
|
|
*
|
|
* 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 <linux/module.h>
|
|
#include <linux/string.h>
|
|
#include <sound/core.h>
|
|
#include <sound/pcm.h>
|
|
#include <sound/pcm_params.h>
|
|
#include <sound/soc.h>
|
|
#include <sound/pcm_drm_eld.h>
|
|
#include <sound/hdmi-codec.h>
|
|
#include <sound/pcm_iec958.h>
|
|
|
|
#include <drm/drm_crtc.h> /* This is only to get MAX_ELD_BYTES */
|
|
|
|
struct hdmi_device {
|
|
struct device *dev;
|
|
struct list_head list;
|
|
int cnt;
|
|
};
|
|
#define pos_to_hdmi_device(pos) container_of((pos), struct hdmi_device, list)
|
|
LIST_HEAD(hdmi_device_list);
|
|
|
|
#define DAI_NAME_SIZE 16
|
|
struct hdmi_codec_priv {
|
|
struct hdmi_codec_pdata hcd;
|
|
struct snd_soc_dai_driver *daidrv;
|
|
struct hdmi_codec_daifmt daifmt[2];
|
|
struct mutex current_stream_lock;
|
|
struct snd_pcm_substream *current_stream;
|
|
struct snd_pcm_hw_constraint_list ratec;
|
|
uint8_t eld[MAX_ELD_BYTES];
|
|
};
|
|
|
|
static const struct snd_soc_dapm_widget hdmi_widgets[] = {
|
|
SND_SOC_DAPM_OUTPUT("TX"),
|
|
};
|
|
|
|
static const struct snd_soc_dapm_route hdmi_routes[] = {
|
|
{ "TX", NULL, "Playback" },
|
|
};
|
|
|
|
enum {
|
|
DAI_ID_I2S = 0,
|
|
DAI_ID_SPDIF,
|
|
};
|
|
|
|
static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_info *uinfo)
|
|
{
|
|
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
|
struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
|
|
|
|
uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
|
|
uinfo->count = sizeof(hcp->eld);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
|
|
struct snd_ctl_elem_value *ucontrol)
|
|
{
|
|
struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
|
|
struct hdmi_codec_priv *hcp = snd_soc_component_get_drvdata(component);
|
|
|
|
memcpy(ucontrol->value.bytes.data, hcp->eld, sizeof(hcp->eld));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct snd_kcontrol_new hdmi_controls[] = {
|
|
{
|
|
.access = SNDRV_CTL_ELEM_ACCESS_READ |
|
|
SNDRV_CTL_ELEM_ACCESS_VOLATILE,
|
|
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
|
|
.name = "ELD",
|
|
.info = hdmi_eld_ctl_info,
|
|
.get = hdmi_eld_ctl_get,
|
|
},
|
|
};
|
|
|
|
static int hdmi_codec_new_stream(struct snd_pcm_substream *substream,
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
|
int ret = 0;
|
|
|
|
mutex_lock(&hcp->current_stream_lock);
|
|
if (!hcp->current_stream) {
|
|
hcp->current_stream = substream;
|
|
} else if (hcp->current_stream != substream) {
|
|
dev_err(dai->dev, "Only one simultaneous stream supported!\n");
|
|
ret = -EINVAL;
|
|
}
|
|
mutex_unlock(&hcp->current_stream_lock);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int hdmi_codec_startup(struct snd_pcm_substream *substream,
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
|
int ret = 0;
|
|
|
|
dev_dbg(dai->dev, "%s()\n", __func__);
|
|
|
|
ret = hdmi_codec_new_stream(substream, dai);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (hcp->hcd.ops->audio_startup) {
|
|
ret = hcp->hcd.ops->audio_startup(dai->dev->parent, hcp->hcd.data);
|
|
if (ret) {
|
|
mutex_lock(&hcp->current_stream_lock);
|
|
hcp->current_stream = NULL;
|
|
mutex_unlock(&hcp->current_stream_lock);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
if (hcp->hcd.ops->get_eld) {
|
|
ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->hcd.data,
|
|
hcp->eld, sizeof(hcp->eld));
|
|
|
|
if (!ret) {
|
|
ret = snd_pcm_hw_constraint_eld(substream->runtime,
|
|
hcp->eld);
|
|
if (ret) {
|
|
mutex_lock(&hcp->current_stream_lock);
|
|
hcp->current_stream = NULL;
|
|
mutex_unlock(&hcp->current_stream_lock);
|
|
return ret;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
|
|
|
dev_dbg(dai->dev, "%s()\n", __func__);
|
|
|
|
WARN_ON(hcp->current_stream != substream);
|
|
|
|
hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data);
|
|
|
|
mutex_lock(&hcp->current_stream_lock);
|
|
hcp->current_stream = NULL;
|
|
mutex_unlock(&hcp->current_stream_lock);
|
|
}
|
|
|
|
static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
|
|
struct snd_pcm_hw_params *params,
|
|
struct snd_soc_dai *dai)
|
|
{
|
|
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
|
struct hdmi_codec_params hp = {
|
|
.iec = {
|
|
.status = { 0 },
|
|
.subcode = { 0 },
|
|
.pad = 0,
|
|
.dig_subframe = { 0 },
|
|
}
|
|
};
|
|
int ret;
|
|
|
|
dev_dbg(dai->dev, "%s() width %d rate %d channels %d\n", __func__,
|
|
params_width(params), params_rate(params),
|
|
params_channels(params));
|
|
|
|
if (params_width(params) > 24)
|
|
params->msbits = 24;
|
|
|
|
ret = snd_pcm_create_iec958_consumer_hw_params(params, hp.iec.status,
|
|
sizeof(hp.iec.status));
|
|
if (ret < 0) {
|
|
dev_err(dai->dev, "Creating IEC958 channel status failed %d\n",
|
|
ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = hdmi_codec_new_stream(substream, dai);
|
|
if (ret)
|
|
return ret;
|
|
|
|
hdmi_audio_infoframe_init(&hp.cea);
|
|
hp.cea.channels = params_channels(params);
|
|
hp.cea.coding_type = HDMI_AUDIO_CODING_TYPE_STREAM;
|
|
hp.cea.sample_size = HDMI_AUDIO_SAMPLE_SIZE_STREAM;
|
|
hp.cea.sample_frequency = HDMI_AUDIO_SAMPLE_FREQUENCY_STREAM;
|
|
|
|
hp.sample_width = params_width(params);
|
|
hp.sample_rate = params_rate(params);
|
|
hp.channels = params_channels(params);
|
|
|
|
return hcp->hcd.ops->hw_params(dai->dev->parent, hcp->hcd.data,
|
|
&hcp->daifmt[dai->id], &hp);
|
|
}
|
|
|
|
static int hdmi_codec_set_fmt(struct snd_soc_dai *dai,
|
|
unsigned int fmt)
|
|
{
|
|
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
|
struct hdmi_codec_daifmt cf = { 0 };
|
|
int ret = 0;
|
|
|
|
dev_dbg(dai->dev, "%s()\n", __func__);
|
|
|
|
if (dai->id == DAI_ID_SPDIF) {
|
|
cf.fmt = HDMI_SPDIF;
|
|
} else {
|
|
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
|
|
case SND_SOC_DAIFMT_CBM_CFM:
|
|
cf.bit_clk_master = 1;
|
|
cf.frame_clk_master = 1;
|
|
break;
|
|
case SND_SOC_DAIFMT_CBS_CFM:
|
|
cf.frame_clk_master = 1;
|
|
break;
|
|
case SND_SOC_DAIFMT_CBM_CFS:
|
|
cf.bit_clk_master = 1;
|
|
break;
|
|
case SND_SOC_DAIFMT_CBS_CFS:
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
|
|
case SND_SOC_DAIFMT_NB_NF:
|
|
break;
|
|
case SND_SOC_DAIFMT_NB_IF:
|
|
cf.frame_clk_inv = 1;
|
|
break;
|
|
case SND_SOC_DAIFMT_IB_NF:
|
|
cf.bit_clk_inv = 1;
|
|
break;
|
|
case SND_SOC_DAIFMT_IB_IF:
|
|
cf.frame_clk_inv = 1;
|
|
cf.bit_clk_inv = 1;
|
|
break;
|
|
}
|
|
|
|
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
|
|
case SND_SOC_DAIFMT_I2S:
|
|
cf.fmt = HDMI_I2S;
|
|
break;
|
|
case SND_SOC_DAIFMT_DSP_A:
|
|
cf.fmt = HDMI_DSP_A;
|
|
break;
|
|
case SND_SOC_DAIFMT_DSP_B:
|
|
cf.fmt = HDMI_DSP_B;
|
|
break;
|
|
case SND_SOC_DAIFMT_RIGHT_J:
|
|
cf.fmt = HDMI_RIGHT_J;
|
|
break;
|
|
case SND_SOC_DAIFMT_LEFT_J:
|
|
cf.fmt = HDMI_LEFT_J;
|
|
break;
|
|
case SND_SOC_DAIFMT_AC97:
|
|
cf.fmt = HDMI_AC97;
|
|
break;
|
|
default:
|
|
dev_err(dai->dev, "Invalid DAI interface format\n");
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
hcp->daifmt[dai->id] = cf;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int hdmi_codec_digital_mute(struct snd_soc_dai *dai, int mute)
|
|
{
|
|
struct hdmi_codec_priv *hcp = snd_soc_dai_get_drvdata(dai);
|
|
|
|
dev_dbg(dai->dev, "%s()\n", __func__);
|
|
|
|
if (hcp->hcd.ops->digital_mute)
|
|
return hcp->hcd.ops->digital_mute(dai->dev->parent,
|
|
hcp->hcd.data, mute);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct snd_soc_dai_ops hdmi_dai_ops = {
|
|
.startup = hdmi_codec_startup,
|
|
.shutdown = hdmi_codec_shutdown,
|
|
.hw_params = hdmi_codec_hw_params,
|
|
.set_fmt = hdmi_codec_set_fmt,
|
|
.digital_mute = hdmi_codec_digital_mute,
|
|
};
|
|
|
|
|
|
#define HDMI_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |\
|
|
SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |\
|
|
SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |\
|
|
SNDRV_PCM_RATE_192000)
|
|
|
|
#define SPDIF_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
|
|
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\
|
|
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\
|
|
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE)
|
|
|
|
/*
|
|
* This list is only for formats allowed on the I2S bus. So there is
|
|
* some formats listed that are not supported by HDMI interface. For
|
|
* instance allowing the 32-bit formats enables 24-precision with CPU
|
|
* DAIs that do not support 24-bit formats. If the extra formats cause
|
|
* problems, we should add the video side driver an option to disable
|
|
* them.
|
|
*/
|
|
#define I2S_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\
|
|
SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE |\
|
|
SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE |\
|
|
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S24_BE |\
|
|
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE)
|
|
|
|
static struct snd_soc_dai_driver hdmi_i2s_dai = {
|
|
.id = DAI_ID_I2S,
|
|
.playback = {
|
|
.stream_name = "Playback",
|
|
.channels_min = 2,
|
|
.channels_max = 8,
|
|
.rates = HDMI_RATES,
|
|
.formats = I2S_FORMATS,
|
|
.sig_bits = 24,
|
|
},
|
|
.ops = &hdmi_dai_ops,
|
|
};
|
|
|
|
static const struct snd_soc_dai_driver hdmi_spdif_dai = {
|
|
.id = DAI_ID_SPDIF,
|
|
.playback = {
|
|
.stream_name = "Playback",
|
|
.channels_min = 2,
|
|
.channels_max = 2,
|
|
.rates = HDMI_RATES,
|
|
.formats = SPDIF_FORMATS,
|
|
},
|
|
.ops = &hdmi_dai_ops,
|
|
};
|
|
|
|
static char hdmi_dai_name[][DAI_NAME_SIZE] = {
|
|
"hdmi-hifi.0",
|
|
"hdmi-hifi.1",
|
|
"hdmi-hifi.2",
|
|
"hdmi-hifi.3",
|
|
};
|
|
|
|
static int hdmi_of_xlate_dai_name(struct snd_soc_component *component,
|
|
struct of_phandle_args *args,
|
|
const char **dai_name)
|
|
{
|
|
int id;
|
|
|
|
if (args->args_count)
|
|
id = args->args[0];
|
|
else
|
|
id = 0;
|
|
|
|
if (id < ARRAY_SIZE(hdmi_dai_name)) {
|
|
*dai_name = hdmi_dai_name[id];
|
|
return 0;
|
|
}
|
|
|
|
return -EAGAIN;
|
|
}
|
|
|
|
static struct snd_soc_codec_driver hdmi_codec = {
|
|
.component_driver = {
|
|
.controls = hdmi_controls,
|
|
.num_controls = ARRAY_SIZE(hdmi_controls),
|
|
.dapm_widgets = hdmi_widgets,
|
|
.num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
|
|
.dapm_routes = hdmi_routes,
|
|
.num_dapm_routes = ARRAY_SIZE(hdmi_routes),
|
|
.of_xlate_dai_name = hdmi_of_xlate_dai_name,
|
|
},
|
|
};
|
|
|
|
static int hdmi_codec_probe(struct platform_device *pdev)
|
|
{
|
|
struct hdmi_codec_pdata *hcd = pdev->dev.platform_data;
|
|
struct device *dev = &pdev->dev;
|
|
struct hdmi_codec_priv *hcp;
|
|
struct hdmi_device *hd;
|
|
struct list_head *pos;
|
|
int dai_count, i = 0;
|
|
int ret;
|
|
|
|
dev_dbg(dev, "%s()\n", __func__);
|
|
|
|
if (!hcd) {
|
|
dev_err(dev, "%s: No plalform data\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
dai_count = hcd->i2s + hcd->spdif;
|
|
if (dai_count < 1 || !hcd->ops || !hcd->ops->hw_params ||
|
|
!hcd->ops->audio_shutdown) {
|
|
dev_err(dev, "%s: Invalid parameters\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
hcp = devm_kzalloc(dev, sizeof(*hcp), GFP_KERNEL);
|
|
if (!hcp)
|
|
return -ENOMEM;
|
|
|
|
hd = NULL;
|
|
list_for_each(pos, &hdmi_device_list) {
|
|
struct hdmi_device *tmp = pos_to_hdmi_device(pos);
|
|
|
|
if (tmp->dev == dev->parent) {
|
|
hd = tmp;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!hd) {
|
|
hd = devm_kzalloc(dev, sizeof(*hd), GFP_KERNEL);
|
|
if (!hd)
|
|
return -ENOMEM;
|
|
|
|
hd->dev = dev->parent;
|
|
|
|
list_add_tail(&hd->list, &hdmi_device_list);
|
|
}
|
|
|
|
if (hd->cnt >= ARRAY_SIZE(hdmi_dai_name)) {
|
|
dev_err(dev, "too many hdmi codec are deteced\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
hcp->hcd = *hcd;
|
|
mutex_init(&hcp->current_stream_lock);
|
|
|
|
hcp->daidrv = devm_kzalloc(dev, dai_count * sizeof(*hcp->daidrv),
|
|
GFP_KERNEL);
|
|
if (!hcp->daidrv)
|
|
return -ENOMEM;
|
|
|
|
if (hcd->i2s) {
|
|
hcp->daidrv[i] = hdmi_i2s_dai;
|
|
hcp->daidrv[i].playback.channels_max =
|
|
hcd->max_i2s_channels;
|
|
hcp->daidrv[i].name = hdmi_dai_name[hd->cnt++];
|
|
i++;
|
|
}
|
|
|
|
if (hcd->spdif) {
|
|
hcp->daidrv[i] = hdmi_spdif_dai;
|
|
hcp->daidrv[i].name = hdmi_dai_name[hd->cnt++];
|
|
}
|
|
|
|
ret = snd_soc_register_codec(dev, &hdmi_codec, hcp->daidrv,
|
|
dai_count);
|
|
if (ret) {
|
|
dev_err(dev, "%s: snd_soc_register_codec() failed (%d)\n",
|
|
__func__, ret);
|
|
return ret;
|
|
}
|
|
|
|
dev_set_drvdata(dev, hcp);
|
|
return 0;
|
|
}
|
|
|
|
static int hdmi_codec_remove(struct platform_device *pdev)
|
|
{
|
|
snd_soc_unregister_codec(&pdev->dev);
|
|
return 0;
|
|
}
|
|
|
|
static struct platform_driver hdmi_codec_driver = {
|
|
.driver = {
|
|
.name = HDMI_CODEC_DRV_NAME,
|
|
},
|
|
.probe = hdmi_codec_probe,
|
|
.remove = hdmi_codec_remove,
|
|
};
|
|
|
|
module_platform_driver(hdmi_codec_driver);
|
|
|
|
MODULE_AUTHOR("Jyri Sarha <jsarha@ti.com>");
|
|
MODULE_DESCRIPTION("HDMI Audio Codec Driver");
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_ALIAS("platform:" HDMI_CODEC_DRV_NAME);
|