Commit be87cfb4 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'sound-3.5' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound

Pull sound update from Takashi Iwai:
 "This is the second updates for 3.5-rc1.  It's mainly for OMAP4 HDMI
  updates and the device tree updates for OMAP, in addition to a couple
  of PCM accuray improvement and Realtek ALC269VD codec support."

* tag 'sound-3.5' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (21 commits)
  ALSA: hda/realtek - Add new codec support for ALC269VD
  ALSA: core: group read of pointer, tstamp and jiffies
  ASoC: OMAP: HDMI: Rename sound card source file
  ASoC: OMAP: HDMI: Make sound card naming more generic
  ASoC: OMAP: HDMI: Make build config options more generic
  ASoC: OMAP: HDMI: Expand capabilities of the HDMI DAI
  ASoC: OMAP: HDMI: Improve how the display state is verified
  ASoC: OMAP: HDMI: Expand configuration of hw_params
  ASoC: OMAP: HDMI: Use the DSS audio interface
  ASoC: OMAP: HDMI: Create a structure for private data of the CPU DAI
  ASoC: OMAP: HDMI: Change error values in HDMI CPU DAI
  ASoC: OMAP: HDMI: Update the platform device names
  ASoC: omap-abe-twl6040: Introduce driver data for runtime parameters
  ASoC: omap-abe-twl6040: Move Digital Mic widget into dapm table
  ASoC: omap-abe-twl6040: Keep only one snd_soc_dai_link structure
  ASoC: omap-dmic: Add device tree bindings
  ASoC: omap-mcpdm: Add device tree bindings
  ASoC: omap-mcbsp: buffer size constraint only applies to playback stream
  ASoC: omap-mcbsp: Use the common interrupt line if supported by the SoC
  ASoC: omap-mcbsp: Remove unused FRAME dma_op_mode
  ...
parents 58823de9 adcc70b2
* Texas Instruments OMAP4+ Digital Microphone Module
Required properties:
- compatible: "ti,omap4-dmic"
- reg: Register location and size as an array:
<MPU access base address, size>,
<L3 interconnect address, size>;
- interrupts: Interrupt number for DMIC
- interrupt-parent: The parent interrupt controller
- ti,hwmods: Name of the hwmod associated with OMAP dmic IP
Example:
dmic: dmic@4012e000 {
compatible = "ti,omap4-dmic";
reg = <0x4012e000 0x7f>, /* MPU private access */
<0x4902e000 0x7f>; /* L3 Interconnect */
interrupts = <0 114 0x4>;
interrupt-parent = <&gic>;
ti,hwmods = "dmic";
};
* Texas Instruments OMAP4+ McPDM
Required properties:
- compatible: "ti,omap4-mcpdm"
- reg: Register location and size as an array:
<MPU access base address, size>,
<L3 interconnect address, size>;
- interrupts: Interrupt number for McPDM
- interrupt-parent: The parent interrupt controller
- ti,hwmods: Name of the hwmod associated to the McPDM
Example:
mcpdm: mcpdm@40132000 {
compatible = "ti,omap4-mcpdm";
reg = <0x40132000 0x7f>, /* MPU private access */
<0x49032000 0x7f>; /* L3 Interconnect */
interrupts = <0 112 0x4>;
interrupt-parent = <&gic>;
ti,hwmods = "mcpdm";
};
...@@ -313,9 +313,22 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, ...@@ -313,9 +313,22 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base; snd_pcm_uframes_t old_hw_ptr, new_hw_ptr, hw_base;
snd_pcm_sframes_t hdelta, delta; snd_pcm_sframes_t hdelta, delta;
unsigned long jdelta; unsigned long jdelta;
unsigned long curr_jiffies;
struct timespec curr_tstamp;
old_hw_ptr = runtime->status->hw_ptr; old_hw_ptr = runtime->status->hw_ptr;
/*
* group pointer, time and jiffies reads to allow for more
* accurate correlations/corrections.
* The values are stored at the end of this routine after
* corrections for hw_ptr position
*/
pos = substream->ops->pointer(substream); pos = substream->ops->pointer(substream);
curr_jiffies = jiffies;
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
snd_pcm_gettime(runtime, (struct timespec *)&curr_tstamp);
if (pos == SNDRV_PCM_POS_XRUN) { if (pos == SNDRV_PCM_POS_XRUN) {
xrun(substream); xrun(substream);
return -EPIPE; return -EPIPE;
...@@ -343,7 +356,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, ...@@ -343,7 +356,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
delta = runtime->hw_ptr_interrupt + runtime->period_size; delta = runtime->hw_ptr_interrupt + runtime->period_size;
if (delta > new_hw_ptr) { if (delta > new_hw_ptr) {
/* check for double acknowledged interrupts */ /* check for double acknowledged interrupts */
hdelta = jiffies - runtime->hw_ptr_jiffies; hdelta = curr_jiffies - runtime->hw_ptr_jiffies;
if (hdelta > runtime->hw_ptr_buffer_jiffies/2) { if (hdelta > runtime->hw_ptr_buffer_jiffies/2) {
hw_base += runtime->buffer_size; hw_base += runtime->buffer_size;
if (hw_base >= runtime->boundary) if (hw_base >= runtime->boundary)
...@@ -388,7 +401,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, ...@@ -388,7 +401,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
* Without regular period interrupts, we have to check * Without regular period interrupts, we have to check
* the elapsed time to detect xruns. * the elapsed time to detect xruns.
*/ */
jdelta = jiffies - runtime->hw_ptr_jiffies; jdelta = curr_jiffies - runtime->hw_ptr_jiffies;
if (jdelta < runtime->hw_ptr_buffer_jiffies / 2) if (jdelta < runtime->hw_ptr_buffer_jiffies / 2)
goto no_delta_check; goto no_delta_check;
hdelta = jdelta - delta * HZ / runtime->rate; hdelta = jdelta - delta * HZ / runtime->rate;
...@@ -430,7 +443,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, ...@@ -430,7 +443,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
if (hdelta < runtime->delay) if (hdelta < runtime->delay)
goto no_jiffies_check; goto no_jiffies_check;
hdelta -= runtime->delay; hdelta -= runtime->delay;
jdelta = jiffies - runtime->hw_ptr_jiffies; jdelta = curr_jiffies - runtime->hw_ptr_jiffies;
if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) { if (((hdelta * HZ) / runtime->rate) > jdelta + HZ/100) {
delta = jdelta / delta = jdelta /
(((runtime->period_size * HZ) / runtime->rate) (((runtime->period_size * HZ) / runtime->rate)
...@@ -492,9 +505,9 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream, ...@@ -492,9 +505,9 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
} }
runtime->hw_ptr_base = hw_base; runtime->hw_ptr_base = hw_base;
runtime->status->hw_ptr = new_hw_ptr; runtime->status->hw_ptr = new_hw_ptr;
runtime->hw_ptr_jiffies = jiffies; runtime->hw_ptr_jiffies = curr_jiffies;
if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE) if (runtime->tstamp_mode == SNDRV_PCM_TSTAMP_ENABLE)
snd_pcm_gettime(runtime, (struct timespec *)&runtime->status->tstamp); runtime->status->tstamp = curr_tstamp;
return snd_pcm_update_state(substream, runtime); return snd_pcm_update_state(substream, runtime);
} }
......
...@@ -2368,6 +2368,7 @@ static struct alc_codec_rename_table rename_tbl[] = { ...@@ -2368,6 +2368,7 @@ static struct alc_codec_rename_table rename_tbl[] = {
{ 0x10ec0269, 0xffff, 0xa023, "ALC259" }, { 0x10ec0269, 0xffff, 0xa023, "ALC259" },
{ 0x10ec0269, 0xffff, 0x6023, "ALC281X" }, { 0x10ec0269, 0xffff, 0x6023, "ALC281X" },
{ 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" }, { 0x10ec0269, 0x00f0, 0x0020, "ALC269VC" },
{ 0x10ec0269, 0x00f0, 0x0030, "ALC269VD" },
{ 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" }, { 0x10ec0887, 0x00f0, 0x0030, "ALC887-VD" },
{ 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" }, { 0x10ec0888, 0x00f0, 0x0030, "ALC888-VD" },
{ 0x10ec0888, 0xf0f0, 0x3020, "ALC886" }, { 0x10ec0888, 0xf0f0, 0x3020, "ALC886" },
...@@ -5614,6 +5615,7 @@ enum { ...@@ -5614,6 +5615,7 @@ enum {
ALC269_TYPE_ALC269VA, ALC269_TYPE_ALC269VA,
ALC269_TYPE_ALC269VB, ALC269_TYPE_ALC269VB,
ALC269_TYPE_ALC269VC, ALC269_TYPE_ALC269VC,
ALC269_TYPE_ALC269VD,
}; };
/* /*
...@@ -5625,8 +5627,21 @@ static int alc269_parse_auto_config(struct hda_codec *codec) ...@@ -5625,8 +5627,21 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
static const hda_nid_t alc269_ssids[] = { 0, 0x1b, 0x14, 0x21 }; static const hda_nid_t alc269_ssids[] = { 0, 0x1b, 0x14, 0x21 };
static const hda_nid_t alc269va_ssids[] = { 0x15, 0x1b, 0x14, 0 }; static const hda_nid_t alc269va_ssids[] = { 0x15, 0x1b, 0x14, 0 };
struct alc_spec *spec = codec->spec; struct alc_spec *spec = codec->spec;
const hda_nid_t *ssids = spec->codec_variant == ALC269_TYPE_ALC269VA ? const hda_nid_t *ssids;
alc269va_ssids : alc269_ssids;
switch (spec->codec_variant) {
case ALC269_TYPE_ALC269VA:
case ALC269_TYPE_ALC269VC:
ssids = alc269va_ssids;
break;
case ALC269_TYPE_ALC269VB:
case ALC269_TYPE_ALC269VD:
ssids = alc269_ssids;
break;
default:
ssids = alc269_ssids;
break;
}
return alc_parse_auto_config(codec, alc269_ignore, ssids); return alc_parse_auto_config(codec, alc269_ignore, ssids);
} }
...@@ -5643,6 +5658,11 @@ static void alc269_toggle_power_output(struct hda_codec *codec, int power_up) ...@@ -5643,6 +5658,11 @@ static void alc269_toggle_power_output(struct hda_codec *codec, int power_up)
static void alc269_shutup(struct hda_codec *codec) static void alc269_shutup(struct hda_codec *codec)
{ {
struct alc_spec *spec = codec->spec;
if (spec->codec_variant != ALC269_TYPE_ALC269VB)
return;
if ((alc_get_coef0(codec) & 0x00ff) == 0x017) if ((alc_get_coef0(codec) & 0x00ff) == 0x017)
alc269_toggle_power_output(codec, 0); alc269_toggle_power_output(codec, 0);
if ((alc_get_coef0(codec) & 0x00ff) == 0x018) { if ((alc_get_coef0(codec) & 0x00ff) == 0x018) {
...@@ -5654,19 +5674,24 @@ static void alc269_shutup(struct hda_codec *codec) ...@@ -5654,19 +5674,24 @@ static void alc269_shutup(struct hda_codec *codec)
#ifdef CONFIG_PM #ifdef CONFIG_PM
static int alc269_resume(struct hda_codec *codec) static int alc269_resume(struct hda_codec *codec)
{ {
if ((alc_get_coef0(codec) & 0x00ff) == 0x018) { struct alc_spec *spec = codec->spec;
if (spec->codec_variant == ALC269_TYPE_ALC269VB ||
(alc_get_coef0(codec) & 0x00ff) == 0x018) {
alc269_toggle_power_output(codec, 0); alc269_toggle_power_output(codec, 0);
msleep(150); msleep(150);
} }
codec->patch_ops.init(codec); codec->patch_ops.init(codec);
if ((alc_get_coef0(codec) & 0x00ff) == 0x017) { if (spec->codec_variant == ALC269_TYPE_ALC269VB ||
(alc_get_coef0(codec) & 0x00ff) == 0x017) {
alc269_toggle_power_output(codec, 1); alc269_toggle_power_output(codec, 1);
msleep(200); msleep(200);
} }
if ((alc_get_coef0(codec) & 0x00ff) == 0x018) if (spec->codec_variant == ALC269_TYPE_ALC269VB ||
(alc_get_coef0(codec) & 0x00ff) == 0x018)
alc269_toggle_power_output(codec, 1); alc269_toggle_power_output(codec, 1);
snd_hda_codec_resume_amp(codec); snd_hda_codec_resume_amp(codec);
...@@ -6081,6 +6106,9 @@ static int patch_alc269(struct hda_codec *codec) ...@@ -6081,6 +6106,9 @@ static int patch_alc269(struct hda_codec *codec)
err = alc_codec_rename(codec, "ALC3202"); err = alc_codec_rename(codec, "ALC3202");
spec->codec_variant = ALC269_TYPE_ALC269VC; spec->codec_variant = ALC269_TYPE_ALC269VC;
break; break;
case 0x0030:
spec->codec_variant = ALC269_TYPE_ALC269VD;
break;
default: default:
alc_fix_pll_init(codec, 0x20, 0x04, 15); alc_fix_pll_init(codec, 0x20, 0x04, 15);
} }
......
...@@ -109,11 +109,12 @@ config SND_OMAP_SOC_OMAP_ABE_TWL6040 ...@@ -109,11 +109,12 @@ config SND_OMAP_SOC_OMAP_ABE_TWL6040
- PandaBoard (4430) - PandaBoard (4430)
- PandaBoardES (4460) - PandaBoardES (4460)
config SND_OMAP_SOC_OMAP4_HDMI config SND_OMAP_SOC_OMAP_HDMI
tristate "SoC Audio support for Texas Instruments OMAP4 HDMI" tristate "SoC Audio support for Texas Instruments OMAP HDMI"
depends on SND_OMAP_SOC && OMAP4_DSS_HDMI && OMAP2_DSS && ARCH_OMAP4 depends on SND_OMAP_SOC && OMAP4_DSS_HDMI && OMAP2_DSS
select SND_OMAP_SOC_HDMI select SND_OMAP_SOC_HDMI
select SND_SOC_OMAP_HDMI_CODEC select SND_SOC_OMAP_HDMI_CODEC
select OMAP4_DSS_HDMI_AUDIO
help help
Say Y if you want to add support for SoC HDMI audio on Texas Instruments Say Y if you want to add support for SoC HDMI audio on Texas Instruments
OMAP4 chips OMAP4 chips
......
...@@ -25,7 +25,7 @@ snd-soc-omap3pandora-objs := omap3pandora.o ...@@ -25,7 +25,7 @@ snd-soc-omap3pandora-objs := omap3pandora.o
snd-soc-omap3beagle-objs := omap3beagle.o snd-soc-omap3beagle-objs := omap3beagle.o
snd-soc-zoom2-objs := zoom2.o snd-soc-zoom2-objs := zoom2.o
snd-soc-igep0020-objs := igep0020.o snd-soc-igep0020-objs := igep0020.o
snd-soc-omap4-hdmi-objs := omap4-hdmi-card.o snd-soc-omap-hdmi-card-objs := omap-hdmi-card.o
obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o obj-$(CONFIG_SND_OMAP_SOC_N810) += snd-soc-n810.o
obj-$(CONFIG_SND_OMAP_SOC_RX51) += snd-soc-rx51.o obj-$(CONFIG_SND_OMAP_SOC_RX51) += snd-soc-rx51.o
...@@ -41,4 +41,4 @@ obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o ...@@ -41,4 +41,4 @@ obj-$(CONFIG_SND_OMAP_SOC_OMAP3_PANDORA) += snd-soc-omap3pandora.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o obj-$(CONFIG_SND_OMAP_SOC_OMAP3_BEAGLE) += snd-soc-omap3beagle.o
obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o obj-$(CONFIG_SND_OMAP_SOC_ZOOM2) += snd-soc-zoom2.o
obj-$(CONFIG_SND_OMAP_SOC_IGEP0020) += snd-soc-igep0020.o obj-$(CONFIG_SND_OMAP_SOC_IGEP0020) += snd-soc-igep0020.o
obj-$(CONFIG_SND_OMAP_SOC_OMAP4_HDMI) += snd-soc-omap4-hdmi.o obj-$(CONFIG_SND_OMAP_SOC_OMAP_HDMI) += snd-soc-omap-hdmi-card.o
...@@ -109,6 +109,47 @@ static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp) ...@@ -109,6 +109,47 @@ static void omap_mcbsp_dump_reg(struct omap_mcbsp *mcbsp)
dev_dbg(mcbsp->dev, "***********************\n"); dev_dbg(mcbsp->dev, "***********************\n");
} }
static irqreturn_t omap_mcbsp_irq_handler(int irq, void *dev_id)
{
struct omap_mcbsp *mcbsp = dev_id;
u16 irqst;
irqst = MCBSP_READ(mcbsp, IRQST);
dev_dbg(mcbsp->dev, "IRQ callback : 0x%x\n", irqst);
if (irqst & RSYNCERREN)
dev_err(mcbsp->dev, "RX Frame Sync Error!\n");
if (irqst & RFSREN)
dev_dbg(mcbsp->dev, "RX Frame Sync\n");
if (irqst & REOFEN)
dev_dbg(mcbsp->dev, "RX End Of Frame\n");
if (irqst & RRDYEN)
dev_dbg(mcbsp->dev, "RX Buffer Threshold Reached\n");
if (irqst & RUNDFLEN)
dev_err(mcbsp->dev, "RX Buffer Underflow!\n");
if (irqst & ROVFLEN)
dev_err(mcbsp->dev, "RX Buffer Overflow!\n");
if (irqst & XSYNCERREN)
dev_err(mcbsp->dev, "TX Frame Sync Error!\n");
if (irqst & XFSXEN)
dev_dbg(mcbsp->dev, "TX Frame Sync\n");
if (irqst & XEOFEN)
dev_dbg(mcbsp->dev, "TX End Of Frame\n");
if (irqst & XRDYEN)
dev_dbg(mcbsp->dev, "TX Buffer threshold Reached\n");
if (irqst & XUNDFLEN)
dev_err(mcbsp->dev, "TX Buffer Underflow!\n");
if (irqst & XOVFLEN)
dev_err(mcbsp->dev, "TX Buffer Overflow!\n");
if (irqst & XEMPTYEOFEN)
dev_dbg(mcbsp->dev, "TX Buffer empty at end of frame\n");
MCBSP_WRITE(mcbsp, IRQST, irqst);
return IRQ_HANDLED;
}
static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id) static irqreturn_t omap_mcbsp_tx_irq_handler(int irq, void *dev_id)
{ {
struct omap_mcbsp *mcbsp_tx = dev_id; struct omap_mcbsp *mcbsp_tx = dev_id;
...@@ -176,6 +217,10 @@ void omap_mcbsp_config(struct omap_mcbsp *mcbsp, ...@@ -176,6 +217,10 @@ void omap_mcbsp_config(struct omap_mcbsp *mcbsp,
/* Enable wakeup behavior */ /* Enable wakeup behavior */
if (mcbsp->pdata->has_wakeup) if (mcbsp->pdata->has_wakeup)
MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN); MCBSP_WRITE(mcbsp, WAKEUPEN, XRDYEN | RRDYEN);
/* Enable TX/RX sync error interrupts by default */
if (mcbsp->irq)
MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN);
} }
/** /**
...@@ -489,23 +534,25 @@ int omap_mcbsp_request(struct omap_mcbsp *mcbsp) ...@@ -489,23 +534,25 @@ int omap_mcbsp_request(struct omap_mcbsp *mcbsp)
MCBSP_WRITE(mcbsp, SPCR1, 0); MCBSP_WRITE(mcbsp, SPCR1, 0);
MCBSP_WRITE(mcbsp, SPCR2, 0); MCBSP_WRITE(mcbsp, SPCR2, 0);
err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, if (mcbsp->irq) {
0, "McBSP", (void *)mcbsp); err = request_irq(mcbsp->irq, omap_mcbsp_irq_handler, 0,
if (err != 0) { "McBSP", (void *)mcbsp);
dev_err(mcbsp->dev, "Unable to request TX IRQ %d " if (err != 0) {
"for McBSP%d\n", mcbsp->tx_irq, dev_err(mcbsp->dev, "Unable to request IRQ\n");
mcbsp->id); goto err_clk_disable;
goto err_clk_disable; }
} } else {
err = request_irq(mcbsp->tx_irq, omap_mcbsp_tx_irq_handler, 0,
"McBSP TX", (void *)mcbsp);
if (err != 0) {
dev_err(mcbsp->dev, "Unable to request TX IRQ\n");
goto err_clk_disable;
}
if (mcbsp->rx_irq) { err = request_irq(mcbsp->rx_irq, omap_mcbsp_rx_irq_handler, 0,
err = request_irq(mcbsp->rx_irq, "McBSP RX", (void *)mcbsp);
omap_mcbsp_rx_irq_handler,
0, "McBSP", (void *)mcbsp);
if (err != 0) { if (err != 0) {
dev_err(mcbsp->dev, "Unable to request RX IRQ %d " dev_err(mcbsp->dev, "Unable to request RX IRQ\n");
"for McBSP%d\n", mcbsp->rx_irq,
mcbsp->id);
goto err_free_irq; goto err_free_irq;
} }
} }
...@@ -542,9 +589,16 @@ void omap_mcbsp_free(struct omap_mcbsp *mcbsp) ...@@ -542,9 +589,16 @@ void omap_mcbsp_free(struct omap_mcbsp *mcbsp)
if (mcbsp->pdata->has_wakeup) if (mcbsp->pdata->has_wakeup)
MCBSP_WRITE(mcbsp, WAKEUPEN, 0); MCBSP_WRITE(mcbsp, WAKEUPEN, 0);
if (mcbsp->rx_irq) /* Disable interrupt requests */
if (mcbsp->irq)
MCBSP_WRITE(mcbsp, IRQEN, 0);
if (mcbsp->irq) {
free_irq(mcbsp->irq, (void *)mcbsp);
} else {
free_irq(mcbsp->rx_irq, (void *)mcbsp); free_irq(mcbsp->rx_irq, (void *)mcbsp);
free_irq(mcbsp->tx_irq, (void *)mcbsp); free_irq(mcbsp->tx_irq, (void *)mcbsp);
}
reg_cache = mcbsp->reg_cache; reg_cache = mcbsp->reg_cache;
...@@ -754,7 +808,7 @@ THRESHOLD_PROP_BUILDER(max_tx_thres); ...@@ -754,7 +808,7 @@ THRESHOLD_PROP_BUILDER(max_tx_thres);
THRESHOLD_PROP_BUILDER(max_rx_thres); THRESHOLD_PROP_BUILDER(max_rx_thres);
static const char *dma_op_modes[] = { static const char *dma_op_modes[] = {
"element", "threshold", "frame", "element", "threshold",
}; };
static ssize_t dma_op_mode_show(struct device *dev, static ssize_t dma_op_mode_show(struct device *dev,
...@@ -949,13 +1003,24 @@ int __devinit omap_mcbsp_init(struct platform_device *pdev) ...@@ -949,13 +1003,24 @@ int __devinit omap_mcbsp_init(struct platform_device *pdev)
else else
mcbsp->phys_dma_base = res->start; mcbsp->phys_dma_base = res->start;
mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx"); /*
mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx"); * OMAP1, 2 uses two interrupt lines: TX, RX
* OMAP2430, OMAP3 SoC have combined IRQ line as well.
/* From OMAP4 there will be a single irq line */ * OMAP4 and newer SoC only have the combined IRQ line.
if (mcbsp->tx_irq == -ENXIO) { * Use the combined IRQ if available since it gives better debugging
mcbsp->tx_irq = platform_get_irq(pdev, 0); * possibilities.
mcbsp->rx_irq = 0; */
mcbsp->irq = platform_get_irq_byname(pdev, "common");
if (mcbsp->irq == -ENXIO) {
mcbsp->tx_irq = platform_get_irq_byname(pdev, "tx");
if (mcbsp->tx_irq == -ENXIO) {
mcbsp->irq = platform_get_irq(pdev, 0);
mcbsp->tx_irq = 0;
} else {
mcbsp->rx_irq = platform_get_irq_byname(pdev, "rx");
mcbsp->irq = 0;
}
} }
res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx"); res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
......
...@@ -217,17 +217,20 @@ enum { ...@@ -217,17 +217,20 @@ enum {
/********************** McBSP DMA operating modes **************************/ /********************** McBSP DMA operating modes **************************/
#define MCBSP_DMA_MODE_ELEMENT 0 #define MCBSP_DMA_MODE_ELEMENT 0
#define MCBSP_DMA_MODE_THRESHOLD 1 #define MCBSP_DMA_MODE_THRESHOLD 1
#define MCBSP_DMA_MODE_FRAME 2
/********************** McBSP WAKEUPEN bit definitions *********************/ /********************** McBSP WAKEUPEN/IRQST/IRQEN bit definitions *********/
#define RSYNCERREN BIT(0) #define RSYNCERREN BIT(0)
#define RFSREN BIT(1) #define RFSREN BIT(1)
#define REOFEN BIT(2) #define REOFEN BIT(2)
#define RRDYEN BIT(3) #define RRDYEN BIT(3)
#define RUNDFLEN BIT(4)
#define ROVFLEN BIT(5)
#define XSYNCERREN BIT(7) #define XSYNCERREN BIT(7)
#define XFSXEN BIT(8) #define XFSXEN BIT(8)
#define XEOFEN BIT(9) #define XEOFEN BIT(9)
#define XRDYEN BIT(10) #define XRDYEN BIT(10)
#define XUNDFLEN BIT(11)
#define XOVFLEN BIT(12)
#define XEMPTYEOFEN BIT(14) #define XEMPTYEOFEN BIT(14)
/* Clock signal muxing options */ /* Clock signal muxing options */
...@@ -295,6 +298,7 @@ struct omap_mcbsp { ...@@ -295,6 +298,7 @@ struct omap_mcbsp {
int configured; int configured;
u8 free; u8 free;
int irq;
int rx_irq; int rx_irq;
int tx_irq; int tx_irq;
......
...@@ -40,6 +40,11 @@ ...@@ -40,6 +40,11 @@
#include "omap-pcm.h" #include "omap-pcm.h"
#include "../codecs/twl6040.h" #include "../codecs/twl6040.h"
struct abe_twl6040 {
int jack_detection; /* board can detect jack events */
int mclk_freq; /* MCLK frequency speed for twl6040 */
};
static int omap_abe_hw_params(struct snd_pcm_substream *substream, static int omap_abe_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params) struct snd_pcm_hw_params *params)
{ {
...@@ -47,13 +52,13 @@ static int omap_abe_hw_params(struct snd_pcm_substream *substream, ...@@ -47,13 +52,13 @@ static int omap_abe_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct snd_soc_codec *codec = rtd->codec; struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_card *card = codec->card; struct snd_soc_card *card = codec->card;
struct omap_abe_twl6040_data *pdata = dev_get_platdata(card->dev); struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
int clk_id, freq; int clk_id, freq;
int ret; int ret;
clk_id = twl6040_get_clk_id(rtd->codec); clk_id = twl6040_get_clk_id(rtd->codec);
if (clk_id == TWL6040_SYSCLK_SEL_HPPLL) if (clk_id == TWL6040_SYSCLK_SEL_HPPLL)
freq = pdata->mclk_freq; freq = priv->mclk_freq;
else if (clk_id == TWL6040_SYSCLK_SEL_LPPLL) else if (clk_id == TWL6040_SYSCLK_SEL_LPPLL)
freq = 32768; freq = 32768;
else else
...@@ -128,6 +133,9 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = { ...@@ -128,6 +133,9 @@ static const struct snd_soc_dapm_widget twl6040_dapm_widgets[] = {
SND_SOC_DAPM_MIC("Main Handset Mic", NULL), SND_SOC_DAPM_MIC("Main Handset Mic", NULL),
SND_SOC_DAPM_MIC("Sub Handset Mic", NULL), SND_SOC_DAPM_MIC("Sub Handset Mic", NULL),
SND_SOC_DAPM_LINE("Line In", NULL), SND_SOC_DAPM_LINE("Line In", NULL),
/* Digital microphones */
SND_SOC_DAPM_MIC("Digital Mic", NULL),
}; };
static const struct snd_soc_dapm_route audio_map[] = { static const struct snd_soc_dapm_route audio_map[] = {
...@@ -173,6 +181,7 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd) ...@@ -173,6 +181,7 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
struct snd_soc_card *card = codec->card; struct snd_soc_card *card = codec->card;
struct snd_soc_dapm_context *dapm = &codec->dapm; struct snd_soc_dapm_context *dapm = &codec->dapm;
struct omap_abe_twl6040_data *pdata = dev_get_platdata(card->dev); struct omap_abe_twl6040_data *pdata = dev_get_platdata(card->dev);
struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
int hs_trim; int hs_trim;
int ret = 0; int ret = 0;
...@@ -196,7 +205,7 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd) ...@@ -196,7 +205,7 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
TWL6040_HSF_TRIM_RIGHT(hs_trim)); TWL6040_HSF_TRIM_RIGHT(hs_trim));
/* Headset jack detection only if it is supported */ /* Headset jack detection only if it is supported */
if (pdata->jack_detection) { if (priv->jack_detection) {
ret = snd_soc_jack_new(codec, "Headset Jack", ret = snd_soc_jack_new(codec, "Headset Jack",
SND_JACK_HEADSET, &hs_jack); SND_JACK_HEADSET, &hs_jack);
if (ret) if (ret)
...@@ -210,10 +219,6 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd) ...@@ -210,10 +219,6 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
return ret; return ret;
} }
static const struct snd_soc_dapm_widget dmic_dapm_widgets[] = {
SND_SOC_DAPM_MIC("Digital Mic", NULL),
};
static const struct snd_soc_dapm_route dmic_audio_map[] = { static const struct snd_soc_dapm_route dmic_audio_map[] = {
{"DMic", NULL, "Digital Mic"}, {"DMic", NULL, "Digital Mic"},
{"Digital Mic", NULL, "Digital Mic1 Bias"}, {"Digital Mic", NULL, "Digital Mic1 Bias"},
...@@ -223,19 +228,13 @@ static int omap_abe_dmic_init(struct snd_soc_pcm_runtime *rtd) ...@@ -223,19 +228,13 @@ static int omap_abe_dmic_init(struct snd_soc_pcm_runtime *rtd)
{ {
struct snd_soc_codec *codec = rtd->codec; struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm; struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret;
ret = snd_soc_dapm_new_controls(dapm, dmic_dapm_widgets,
ARRAY_SIZE(dmic_dapm_widgets));
if (ret)
return ret;
return snd_soc_dapm_add_routes(dapm, dmic_audio_map, return snd_soc_dapm_add_routes(dapm, dmic_audio_map,
ARRAY_SIZE(dmic_audio_map)); ARRAY_SIZE(dmic_audio_map));
} }
/* Digital audio interface glue - connects codec <--> CPU */ /* Digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link twl6040_dmic_dai[] = { static struct snd_soc_dai_link abe_twl6040_dai_links[] = {
{ {
.name = "TWL6040", .name = "TWL6040",
.stream_name = "TWL6040", .stream_name = "TWL6040",
...@@ -258,19 +257,6 @@ static struct snd_soc_dai_link twl6040_dmic_dai[] = { ...@@ -258,19 +257,6 @@ static struct snd_soc_dai_link twl6040_dmic_dai[] = {
}, },
}; };
static struct snd_soc_dai_link twl6040_only_dai[] = {
{
.name = "TWL6040",
.stream_name = "TWL6040",
.cpu_dai_name = "omap-mcpdm",
.codec_dai_name = "twl6040-legacy",
.platform_name = "omap-pcm-audio",
.codec_name = "twl6040-codec",
.init = omap_abe_twl6040_init,
.ops = &omap_abe_ops,
},
};
/* Audio machine driver */ /* Audio machine driver */
static struct snd_soc_card omap_abe_card = { static struct snd_soc_card omap_abe_card = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
...@@ -285,6 +271,8 @@ static __devinit int omap_abe_probe(struct platform_device *pdev) ...@@ -285,6 +271,8 @@ static __devinit int omap_abe_probe(struct platform_device *pdev)
{ {
struct omap_abe_twl6040_data *pdata = dev_get_platdata(&pdev->dev); struct omap_abe_twl6040_data *pdata = dev_get_platdata(&pdev->dev);
struct snd_soc_card *card = &omap_abe_card; struct snd_soc_card *card = &omap_abe_card;
struct abe_twl6040 *priv;
int num_links = 0;
int ret; int ret;
card->dev = &pdev->dev; card->dev = &pdev->dev;
...@@ -294,6 +282,10 @@ static __devinit int omap_abe_probe(struct platform_device *pdev) ...@@ -294,6 +282,10 @@ static __devinit int omap_abe_probe(struct platform_device *pdev)
return -ENODEV; return -ENODEV;
} }
priv = devm_kzalloc(&pdev->dev, sizeof(struct abe_twl6040), GFP_KERNEL);
if (priv == NULL)
return -ENOMEM;
if (pdata->card_name) { if (pdata->card_name) {
card->name = pdata->card_name; card->name = pdata->card_name;
} else { } else {
...@@ -301,18 +293,24 @@ static __devinit int omap_abe_probe(struct platform_device *pdev) ...@@ -301,18 +293,24 @@ static __devinit int omap_abe_probe(struct platform_device *pdev)
return -ENODEV; return -ENODEV;
} }
if (!pdata->mclk_freq) { priv->jack_detection = pdata->jack_detection;
priv->mclk_freq = pdata->mclk_freq;
if (!priv->mclk_freq) {
dev_err(&pdev->dev, "MCLK frequency missing\n"); dev_err(&pdev->dev, "MCLK frequency missing\n");
return -ENODEV; return -ENODEV;
} }
if (pdata->has_dmic) { if (pdata->has_dmic)
card->dai_link = twl6040_dmic_dai; num_links = 2;
card->num_links = ARRAY_SIZE(twl6040_dmic_dai); else
} else { num_links = 1;
card->dai_link = twl6040_only_dai;
card->num_links = ARRAY_SIZE(twl6040_only_dai); card->dai_link = abe_twl6040_dai_links;
} card->num_links = num_links;
snd_soc_card_set_drvdata(card, priv);
ret = snd_soc_register_card(card); ret = snd_soc_register_card(card);
if (ret) if (ret)
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/of_device.h>
#include <plat/dma.h> #include <plat/dma.h>
#include <sound/core.h> #include <sound/core.h>
...@@ -528,10 +529,17 @@ static int __devexit asoc_dmic_remove(struct platform_device *pdev) ...@@ -528,10 +529,17 @@ static int __devexit asoc_dmic_remove(struct platform_device *pdev)
return 0; return 0;
} }
static const struct of_device_id omap_dmic_of_match[] = {
{ .compatible = "ti,omap4-dmic", },
{ }
};
MODULE_DEVICE_TABLE(of, omap_dmic_of_match);
static struct platform_driver asoc_dmic_driver = { static struct platform_driver asoc_dmic_driver = {
.driver = { .driver = {
.name = "omap-dmic", .name = "omap-dmic",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = omap_dmic_of_match,
}, },
.probe = asoc_dmic_probe, .probe = asoc_dmic_probe,
.remove = __devexit_p(asoc_dmic_remove), .remove = __devexit_p(asoc_dmic_remove),
......
/* /*
* omap4-hdmi-card.c * omap-hdmi-card.c
* *
* OMAP ALSA SoC machine driver for TI OMAP4 HDMI * OMAP ALSA SoC machine driver for TI OMAP HDMI
* Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/
* Author: Ricardo Neri <ricardo.neri@ti.com> * Author: Ricardo Neri <ricardo.neri@ti.com>
* *
...@@ -27,61 +27,27 @@ ...@@ -27,61 +27,27 @@
#include <asm/mach-types.h> #include <asm/mach-types.h>
#include <video/omapdss.h> #include <video/omapdss.h>
#define DRV_NAME "omap4-hdmi-audio" #define DRV_NAME "omap-hdmi-audio"
static int omap4_hdmi_dai_hw_params(struct snd_pcm_substream *substream, static struct snd_soc_dai_link omap_hdmi_dai = {
struct snd_pcm_hw_params *params)
{
int i;
struct omap_overlay_manager *mgr = NULL;
struct device *dev = substream->pcm->card->dev;
/* Find DSS HDMI device */
for (i = 0; i < omap_dss_get_num_overlay_managers(); i++) {
mgr = omap_dss_get_overlay_manager(i);
if (mgr && mgr->device
&& mgr->device->type == OMAP_DISPLAY_TYPE_HDMI)
break;
}
if (i == omap_dss_get_num_overlay_managers()) {
dev_err(dev, "HDMI display device not found!\n");
return -ENODEV;
}
/* Make sure HDMI is power-on to avoid L3 interconnect errors */
if (mgr->device->state != OMAP_DSS_DISPLAY_ACTIVE) {
dev_err(dev, "HDMI display is not active!\n");
return -EIO;
}
return 0;
}
static struct snd_soc_ops omap4_hdmi_dai_ops = {
.hw_params = omap4_hdmi_dai_hw_params,
};
static struct snd_soc_dai_link omap4_hdmi_dai = {
.name = "HDMI", .name = "HDMI",
.stream_name = "HDMI", .stream_name = "HDMI",
.cpu_dai_name = "hdmi-audio-dai", .cpu_dai_name = "omap-hdmi-audio-dai",
.platform_name = "omap-pcm-audio", .platform_name = "omap-pcm-audio",
.codec_name = "omapdss_hdmi", .codec_name = "hdmi-audio-codec",
.codec_dai_name = "hdmi-audio-codec", .codec_dai_name = "omap-hdmi-hifi",
.ops = &omap4_hdmi_dai_ops,
}; };
static struct snd_soc_card snd_soc_omap4_hdmi = { static struct snd_soc_card snd_soc_omap_hdmi = {
.name = "OMAP4HDMI", .name = "OMAPHDMI",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.dai_link = &omap4_hdmi_dai, .dai_link = &omap_hdmi_dai,
.num_links = 1, .num_links = 1,
}; };
static __devinit int omap4_hdmi_probe(struct platform_device *pdev) static __devinit int omap_hdmi_probe(struct platform_device *pdev)
{ {
struct snd_soc_card *card = &snd_soc_omap4_hdmi; struct snd_soc_card *card = &snd_soc_omap_hdmi;
int ret; int ret;
card->dev = &pdev->dev; card->dev = &pdev->dev;
...@@ -95,7 +61,7 @@ static __devinit int omap4_hdmi_probe(struct platform_device *pdev) ...@@ -95,7 +61,7 @@ static __devinit int omap4_hdmi_probe(struct platform_device *pdev)
return 0; return 0;
} }
static int __devexit omap4_hdmi_remove(struct platform_device *pdev) static int __devexit omap_hdmi_remove(struct platform_device *pdev)
{ {
struct snd_soc_card *card = platform_get_drvdata(pdev); struct snd_soc_card *card = platform_get_drvdata(pdev);
...@@ -104,18 +70,18 @@ static int __devexit omap4_hdmi_remove(struct platform_device *pdev) ...@@ -104,18 +70,18 @@ static int __devexit omap4_hdmi_remove(struct platform_device *pdev)
return 0; return 0;
} }
static struct platform_driver omap4_hdmi_driver = { static struct platform_driver omap_hdmi_driver = {
.driver = { .driver = {
.name = "omap4-hdmi-audio", .name = DRV_NAME,
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
.probe = omap4_hdmi_probe, .probe = omap_hdmi_probe,
.remove = __devexit_p(omap4_hdmi_remove), .remove = __devexit_p(omap_hdmi_remove),
}; };
module_platform_driver(omap4_hdmi_driver); module_platform_driver(omap_hdmi_driver);
MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>"); MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>");
MODULE_DESCRIPTION("OMAP4 HDMI machine ASoC driver"); MODULE_DESCRIPTION("OMAP HDMI machine ASoC driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DRV_NAME); MODULE_ALIAS("platform:" DRV_NAME);
...@@ -30,21 +30,28 @@ ...@@ -30,21 +30,28 @@
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/initval.h> #include <sound/initval.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/asound.h>
#include <sound/asoundef.h>
#include <video/omapdss.h>
#include <plat/dma.h> #include <plat/dma.h>
#include "omap-pcm.h" #include "omap-pcm.h"
#include "omap-hdmi.h" #include "omap-hdmi.h"
#define DRV_NAME "hdmi-audio-dai" #define DRV_NAME "omap-hdmi-audio-dai"
static struct omap_pcm_dma_data omap_hdmi_dai_dma_params = { struct hdmi_priv {
.name = "HDMI playback", struct omap_pcm_dma_data dma_params;
.sync_mode = OMAP_DMA_SYNC_PACKET, struct omap_dss_audio dss_audio;
struct snd_aes_iec958 iec;
struct snd_cea_861_aud_if cea;
struct omap_dss_device *dssdev;
}; };
static int omap_hdmi_dai_startup(struct snd_pcm_substream *substream, static int omap_hdmi_dai_startup(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct hdmi_priv *priv = snd_soc_dai_get_drvdata(dai);
int err; int err;
/* /*
* Make sure that the period bytes are multiple of the DMA packet size. * Make sure that the period bytes are multiple of the DMA packet size.
...@@ -52,46 +59,201 @@ static int omap_hdmi_dai_startup(struct snd_pcm_substream *substream, ...@@ -52,46 +59,201 @@ static int omap_hdmi_dai_startup(struct snd_pcm_substream *substream,
*/ */
err = snd_pcm_hw_constraint_step(substream->runtime, 0, err = snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128); SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 128);
if (err < 0) if (err < 0) {
dev_err(dai->dev, "could not apply constraint\n");
return err; return err;
}
if (!priv->dssdev->driver->audio_supported(priv->dssdev)) {
dev_err(dai->dev, "audio not supported\n");
return -ENODEV;
}
return 0; return 0;
} }
static int omap_hdmi_dai_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct hdmi_priv *priv = snd_soc_dai_get_drvdata(dai);
return priv->dssdev->driver->audio_enable(priv->dssdev);
}
static int omap_hdmi_dai_hw_params(struct snd_pcm_substream *substream, static int omap_hdmi_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai) struct snd_soc_dai *dai)
{ {
struct hdmi_priv *priv = snd_soc_dai_get_drvdata(dai);
struct snd_aes_iec958 *iec = &priv->iec;
struct snd_cea_861_aud_if *cea = &priv->cea;
int err = 0; int err = 0;
switch (params_format(params)) { switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE: case SNDRV_PCM_FORMAT_S16_LE:
omap_hdmi_dai_dma_params.packet_size = 16; priv->dma_params.packet_size = 16;
break; break;
case SNDRV_PCM_FORMAT_S24_LE: case SNDRV_PCM_FORMAT_S24_LE:
omap_hdmi_dai_dma_params.packet_size = 32; priv->dma_params.packet_size = 32;
break; break;
default: default:
err = -EINVAL; dev_err(dai->dev, "format not supported!\n");
return -EINVAL;
} }
omap_hdmi_dai_dma_params.data_type = OMAP_DMA_DATA_TYPE_S32; priv->dma_params.data_type = OMAP_DMA_DATA_TYPE_S32;
snd_soc_dai_set_dma_data(dai, substream, snd_soc_dai_set_dma_data(dai, substream,
&omap_hdmi_dai_dma_params); &priv->dma_params);
/*
* fill the IEC-60958 channel status word
*/
/* specify IEC-60958-3 (commercial use) */
iec->status[0] &= ~IEC958_AES0_PROFESSIONAL;
/* specify that the audio is LPCM*/
iec->status[0] &= ~IEC958_AES0_NONAUDIO;
iec->status[0] |= IEC958_AES0_CON_NOT_COPYRIGHT;
iec->status[0] |= IEC958_AES0_CON_EMPHASIS_NONE;
iec->status[0] |= IEC958_AES1_PRO_MODE_NOTID;
iec->status[1] = IEC958_AES1_CON_GENERAL;
iec->status[2] |= IEC958_AES2_CON_SOURCE_UNSPEC;
iec->status[2] |= IEC958_AES2_CON_CHANNEL_UNSPEC;
switch (params_rate(params)) {
case 32000:
iec->status[3] |= IEC958_AES3_CON_FS_32000;
break;
case 44100:
iec->status[3] |= IEC958_AES3_CON_FS_44100;
break;
case 48000:
iec->status[3] |= IEC958_AES3_CON_FS_48000;
break;
case 88200:
iec->status[3] |= IEC958_AES3_CON_FS_88200;
break;
case 96000:
iec->status[3] |= IEC958_AES3_CON_FS_96000;
break;
case 176400:
iec->status[3] |= IEC958_AES3_CON_FS_176400;
break;
case 192000:
iec->status[3] |= IEC958_AES3_CON_FS_192000;
break;
default:
dev_err(dai->dev, "rate not supported!\n");
return -EINVAL;
}
/* specify the clock accuracy */
iec->status[3] |= IEC958_AES3_CON_CLOCK_1000PPM;
/*
* specify the word length. The same word length value can mean
* two different lengths. Hence, we need to specify the maximum
* word length as well.
*/
switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE:
iec->status[4] |= IEC958_AES4_CON_WORDLEN_20_16;
iec->status[4] &= ~IEC958_AES4_CON_MAX_WORDLEN_24;
break;
case SNDRV_PCM_FORMAT_S24_LE:
iec->status[4] |= IEC958_AES4_CON_WORDLEN_24_20;
iec->status[4] |= IEC958_AES4_CON_MAX_WORDLEN_24;
break;
default:
dev_err(dai->dev, "format not supported!\n");
return -EINVAL;
}
/*
* Fill the CEA-861 audio infoframe (see spec for details)
*/
cea->db1_ct_cc = (params_channels(params) - 1)
& CEA861_AUDIO_INFOFRAME_DB1CC;
cea->db1_ct_cc |= CEA861_AUDIO_INFOFRAME_DB1CT_FROM_STREAM;
cea->db2_sf_ss = CEA861_AUDIO_INFOFRAME_DB2SF_FROM_STREAM;
cea->db2_sf_ss |= CEA861_AUDIO_INFOFRAME_DB2SS_FROM_STREAM;
cea->db3 = 0; /* not used, all zeros */
/*
* The OMAP HDMI IP requires to use the 8-channel channel code when
* transmitting more than two channels.
*/
if (params_channels(params) == 2)
cea->db4_ca = 0x0;
else
cea->db4_ca = 0x13;
cea->db5_dminh_lsv = CEA861_AUDIO_INFOFRAME_DB5_DM_INH_PROHIBITED;
/* the expression is trivial but makes clear what we are doing */
cea->db5_dminh_lsv |= (0 & CEA861_AUDIO_INFOFRAME_DB5_LSV);
priv->dss_audio.iec = iec;
priv->dss_audio.cea = cea;
err = priv->dssdev->driver->audio_config(priv->dssdev,
&priv->dss_audio);
return err; return err;
} }
static int omap_hdmi_dai_trigger(struct snd_pcm_substream *substream, int cmd,
struct snd_soc_dai *dai)
{
struct hdmi_priv *priv = snd_soc_dai_get_drvdata(dai);
int err = 0;
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
err = priv->dssdev->driver->audio_start(priv->dssdev);
break;
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
priv->dssdev->driver->audio_stop(priv->dssdev);
break;
default:
err = -EINVAL;
}
return err;
}
static void omap_hdmi_dai_shutdown(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct hdmi_priv *priv = snd_soc_dai_get_drvdata(dai);
priv->dssdev->driver->audio_disable(priv->dssdev);
}
static const struct snd_soc_dai_ops omap_hdmi_dai_ops = { static const struct snd_soc_dai_ops omap_hdmi_dai_ops = {
.startup = omap_hdmi_dai_startup, .startup = omap_hdmi_dai_startup,
.hw_params = omap_hdmi_dai_hw_params, .hw_params = omap_hdmi_dai_hw_params,
.prepare = omap_hdmi_dai_prepare,
.trigger = omap_hdmi_dai_trigger,
.shutdown = omap_hdmi_dai_shutdown,
}; };
static struct snd_soc_dai_driver omap_hdmi_dai = { static struct snd_soc_dai_driver omap_hdmi_dai = {
.playback = { .playback = {
.channels_min = 2, .channels_min = 2,
.channels_max = 2, .channels_max = 8,
.rates = OMAP_HDMI_RATES, .rates = OMAP_HDMI_RATES,
.formats = OMAP_HDMI_FORMATS, .formats = OMAP_HDMI_FORMATS,
}, },
...@@ -102,31 +264,77 @@ static __devinit int omap_hdmi_probe(struct platform_device *pdev) ...@@ -102,31 +264,77 @@ static __devinit int omap_hdmi_probe(struct platform_device *pdev)
{ {
int ret; int ret;
struct resource *hdmi_rsrc; struct resource *hdmi_rsrc;
struct hdmi_priv *hdmi_data;
bool hdmi_dev_found = false;
hdmi_data = devm_kzalloc(&pdev->dev, sizeof(*hdmi_data), GFP_KERNEL);
if (hdmi_data == NULL) {
dev_err(&pdev->dev, "Cannot allocate memory for HDMI data\n");
return -ENOMEM;
}
hdmi_rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 0); hdmi_rsrc = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!hdmi_rsrc) { if (!hdmi_rsrc) {
dev_err(&pdev->dev, "Cannot obtain IORESOURCE_MEM HDMI\n"); dev_err(&pdev->dev, "Cannot obtain IORESOURCE_MEM HDMI\n");
return -EINVAL; return -ENODEV;
} }
omap_hdmi_dai_dma_params.port_addr = hdmi_rsrc->start hdmi_data->dma_params.port_addr = hdmi_rsrc->start
+ OMAP_HDMI_AUDIO_DMA_PORT; + OMAP_HDMI_AUDIO_DMA_PORT;
hdmi_rsrc = platform_get_resource(pdev, IORESOURCE_DMA, 0); hdmi_rsrc = platform_get_resource(pdev, IORESOURCE_DMA, 0);
if (!hdmi_rsrc) { if (!hdmi_rsrc) {
dev_err(&pdev->dev, "Cannot obtain IORESOURCE_DMA HDMI\n"); dev_err(&pdev->dev, "Cannot obtain IORESOURCE_DMA HDMI\n");
return -EINVAL; return -ENODEV;
} }
omap_hdmi_dai_dma_params.dma_req = hdmi_rsrc->start; hdmi_data->dma_params.dma_req = hdmi_rsrc->start;
hdmi_data->dma_params.name = "HDMI playback";
hdmi_data->dma_params.sync_mode = OMAP_DMA_SYNC_PACKET;
/*
* TODO: We assume that there is only one DSS HDMI device. Future
* OMAP implementations may support more than one HDMI devices and
* we should provided separate audio support for all of them.
*/
/* Find an HDMI device. */
for_each_dss_dev(hdmi_data->dssdev) {
omap_dss_get_device(hdmi_data->dssdev);
if (!hdmi_data->dssdev->driver) {
omap_dss_put_device(hdmi_data->dssdev);
continue;
}
if (hdmi_data->dssdev->type == OMAP_DISPLAY_TYPE_HDMI) {
hdmi_dev_found = true;
break;
}
}
if (!hdmi_dev_found) {
dev_err(&pdev->dev, "no driver for HDMI display found\n");
return -ENODEV;
}
dev_set_drvdata(&pdev->dev, hdmi_data);
ret = snd_soc_register_dai(&pdev->dev, &omap_hdmi_dai); ret = snd_soc_register_dai(&pdev->dev, &omap_hdmi_dai);
return ret; return ret;
} }
static int __devexit omap_hdmi_remove(struct platform_device *pdev) static int __devexit omap_hdmi_remove(struct platform_device *pdev)
{ {
struct hdmi_priv *hdmi_data = dev_get_drvdata(&pdev->dev);
snd_soc_unregister_dai(&pdev->dev); snd_soc_unregister_dai(&pdev->dev);
if (hdmi_data == NULL) {
dev_err(&pdev->dev, "cannot obtain HDMi data\n");
return -ENODEV;
}
omap_dss_put_device(hdmi_data->dssdev);
return 0; return 0;
} }
......
...@@ -28,7 +28,9 @@ ...@@ -28,7 +28,9 @@
#define OMAP_HDMI_AUDIO_DMA_PORT 0x8c #define OMAP_HDMI_AUDIO_DMA_PORT 0x8c
#define OMAP_HDMI_RATES (SNDRV_PCM_RATE_32000 | \ #define OMAP_HDMI_RATES (SNDRV_PCM_RATE_32000 | \
SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000) 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 OMAP_HDMI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \ #define OMAP_HDMI_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S24_LE) SNDRV_PCM_FMTBIT_S24_LE)
......
...@@ -71,18 +71,17 @@ static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream) ...@@ -71,18 +71,17 @@ static void omap_mcbsp_set_threshold(struct snd_pcm_substream *substream)
dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
/* TODO: Currently, MODE_ELEMENT == MODE_FRAME */ /*
if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) * Configure McBSP threshold based on either:
/* * packet_size, when the sDMA is in packet mode, or based on the
* Configure McBSP threshold based on either: * period size in THRESHOLD mode, otherwise use McBSP threshold = 1
* packet_size, when the sDMA is in packet mode, or * for mono streams.
* based on the period size. */
*/ if (dma_data->packet_size)
if (dma_data->packet_size) words = dma_data->packet_size;
words = dma_data->packet_size; else if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD)
else words = snd_pcm_lib_period_bytes(substream) /
words = snd_pcm_lib_period_bytes(substream) / (mcbsp->wlen / 8);
(mcbsp->wlen / 8);
else else
words = 1; words = 1;
...@@ -139,13 +138,15 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream, ...@@ -139,13 +138,15 @@ static int omap_mcbsp_dai_startup(struct snd_pcm_substream *substream,
if (mcbsp->pdata->buffer_size) { if (mcbsp->pdata->buffer_size) {
/* /*
* Rule for the buffer size. We should not allow * Rule for the buffer size. We should not allow
* smaller buffer than the FIFO size to avoid underruns * smaller buffer than the FIFO size to avoid underruns.
* This applies only for the playback stream.
*/ */
snd_pcm_hw_rule_add(substream->runtime, 0, if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
SNDRV_PCM_HW_PARAM_BUFFER_SIZE, snd_pcm_hw_rule_add(substream->runtime, 0,
omap_mcbsp_hwrule_min_buffersize, SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
mcbsp, omap_mcbsp_hwrule_min_buffersize,
SNDRV_PCM_HW_PARAM_CHANNELS, -1); mcbsp,
SNDRV_PCM_HW_PARAM_CHANNELS, -1);
/* Make sure, that the period size is always even */ /* Make sure, that the period size is always even */
snd_pcm_hw_constraint_step(substream->runtime, 0, snd_pcm_hw_constraint_step(substream->runtime, 0,
...@@ -230,6 +231,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, ...@@ -230,6 +231,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
unsigned int format, div, framesize, master; unsigned int format, div, framesize, master;
dma_data = &mcbsp->dma_data[substream->stream]; dma_data = &mcbsp->dma_data[substream->stream];
channels = params_channels(params);
switch (params_format(params)) { switch (params_format(params)) {
case SNDRV_PCM_FORMAT_S16_LE: case SNDRV_PCM_FORMAT_S16_LE:
...@@ -245,7 +247,6 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, ...@@ -245,7 +247,6 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
} }
if (mcbsp->pdata->buffer_size) { if (mcbsp->pdata->buffer_size) {
dma_data->set_threshold = omap_mcbsp_set_threshold; dma_data->set_threshold = omap_mcbsp_set_threshold;
/* TODO: Currently, MODE_ELEMENT == MODE_FRAME */
if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) { if (mcbsp->dma_op_mode == MCBSP_DMA_MODE_THRESHOLD) {
int period_words, max_thrsh; int period_words, max_thrsh;
...@@ -283,6 +284,10 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, ...@@ -283,6 +284,10 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
} else { } else {
sync_mode = OMAP_DMA_SYNC_FRAME; sync_mode = OMAP_DMA_SYNC_FRAME;
} }
} else if (channels > 1) {
/* Use packet mode for non mono streams */
pkt_size = channels;
sync_mode = OMAP_DMA_SYNC_PACKET;
} }
} }
...@@ -301,7 +306,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream, ...@@ -301,7 +306,7 @@ static int omap_mcbsp_dai_hw_params(struct snd_pcm_substream *substream,
regs->rcr1 &= ~(RFRLEN1(0x7f) | RWDLEN1(7)); regs->rcr1 &= ~(RFRLEN1(0x7f) | RWDLEN1(7));
regs->xcr1 &= ~(XFRLEN1(0x7f) | XWDLEN1(7)); regs->xcr1 &= ~(XFRLEN1(0x7f) | XWDLEN1(7));
format = mcbsp->fmt & SND_SOC_DAIFMT_FORMAT_MASK; format = mcbsp->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
wpf = channels = params_channels(params); wpf = channels;
if (channels == 2 && (format == SND_SOC_DAIFMT_I2S || if (channels == 2 && (format == SND_SOC_DAIFMT_I2S ||
format == SND_SOC_DAIFMT_LEFT_J)) { format == SND_SOC_DAIFMT_LEFT_J)) {
/* Use dual-phase frames */ /* Use dual-phase frames */
......
...@@ -33,6 +33,7 @@ ...@@ -33,6 +33,7 @@
#include <linux/irq.h> #include <linux/irq.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/of_device.h>
#include <sound/core.h> #include <sound/core.h>
#include <sound/pcm.h> #include <sound/pcm.h>
...@@ -507,10 +508,17 @@ static int __devexit asoc_mcpdm_remove(struct platform_device *pdev) ...@@ -507,10 +508,17 @@ static int __devexit asoc_mcpdm_remove(struct platform_device *pdev)
return 0; return 0;
} }
static const struct of_device_id omap_mcpdm_of_match[] = {
{ .compatible = "ti,omap4-mcpdm", },
{ }
};
MODULE_DEVICE_TABLE(of, omap_mcpdm_of_match);
static struct platform_driver asoc_mcpdm_driver = { static struct platform_driver asoc_mcpdm_driver = {
.driver = { .driver = {
.name = "omap-mcpdm", .name = "omap-mcpdm",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = omap_mcpdm_of_match,
}, },
.probe = asoc_mcpdm_probe, .probe = asoc_mcpdm_probe,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment