Merge remote-tracking branches 'asoc/topic/headers', 'asoc/topic/intel', 'asoc/topic/jz4740', 'asoc/topic/max98090', 'asoc/topic/max98095', 'asoc/topic/mc13783' and 'asoc/topic/multicodec' into asoc-next

This commit is contained in:
Mark Brown 2014-05-22 00:23:54 +01:00
31 changed files with 745 additions and 431 deletions

View file

@ -10,6 +10,9 @@ Optional properties:
- fsl,mc13xxx-uses-touch : Indicate the touchscreen controller is being used - fsl,mc13xxx-uses-touch : Indicate the touchscreen controller is being used
Sub-nodes: Sub-nodes:
- codec: Contain the Audio Codec node.
- adc-port: Contain PMIC SSI port number used for ADC.
- dac-port: Contain PMIC SSI port number used for DAC.
- leds : Contain the led nodes and initial register values in property - leds : Contain the led nodes and initial register values in property
"led-control". Number of register depends of used IC, for MC13783 is 6, "led-control". Number of register depends of used IC, for MC13783 is 6,
for MC13892 is 4, for MC34708 is 1. See datasheet for bits definitions of for MC13892 is 4, for MC34708 is 1. See datasheet for bits definitions of

View file

@ -0,0 +1,16 @@
MAX98095 audio CODEC
This device supports I2C only.
Required properties:
- compatible : "maxim,max98095".
- reg : The I2C address of the device.
Example:
max98095: codec@11 {
compatible = "maxim,max98095";
reg = <0x11>;
};

View file

@ -22,8 +22,6 @@ enum jz4740_dma_request_type {
JZ4740_DMA_TYPE_UART_RECEIVE = 21, JZ4740_DMA_TYPE_UART_RECEIVE = 21,
JZ4740_DMA_TYPE_SPI_TRANSMIT = 22, JZ4740_DMA_TYPE_SPI_TRANSMIT = 22,
JZ4740_DMA_TYPE_SPI_RECEIVE = 23, JZ4740_DMA_TYPE_SPI_RECEIVE = 23,
JZ4740_DMA_TYPE_AIC_TRANSMIT = 24,
JZ4740_DMA_TYPE_AIC_RECEIVE = 25,
JZ4740_DMA_TYPE_MMC_TRANSMIT = 26, JZ4740_DMA_TYPE_MMC_TRANSMIT = 26,
JZ4740_DMA_TYPE_MMC_RECEIVE = 27, JZ4740_DMA_TYPE_MMC_RECEIVE = 27,
JZ4740_DMA_TYPE_TCU = 28, JZ4740_DMA_TYPE_TCU = 28,

View file

@ -425,6 +425,15 @@ static struct platform_device qi_lb60_audio_device = {
.id = -1, .id = -1,
}; };
static struct gpiod_lookup_table qi_lb60_audio_gpio_table = {
.dev_id = "qi-lb60-audio",
.table = {
GPIO_LOOKUP("Bank B", 29, "snd", 0),
GPIO_LOOKUP("Bank D", 4, "amp", 0),
{ },
},
};
static struct platform_device *jz_platform_devices[] __initdata = { static struct platform_device *jz_platform_devices[] __initdata = {
&jz4740_udc_device, &jz4740_udc_device,
&jz4740_udc_xceiv_device, &jz4740_udc_xceiv_device,
@ -461,6 +470,8 @@ static int __init qi_lb60_init_platform_devices(void)
jz4740_adc_device.dev.platform_data = &qi_lb60_battery_pdata; jz4740_adc_device.dev.platform_data = &qi_lb60_battery_pdata;
jz4740_mmc_device.dev.platform_data = &qi_lb60_mmc_pdata; jz4740_mmc_device.dev.platform_data = &qi_lb60_mmc_pdata;
gpiod_add_lookup_table(&qi_lb60_audio_gpio_table);
jz4740_serial_device_register(); jz4740_serial_device_register();
spi_register_board_info(qi_lb60_spi_board_info, spi_register_board_info(qi_lb60_spi_board_info,

View file

@ -673,9 +673,13 @@ int mc13xxx_common_init(struct device *dev)
if (mc13xxx->flags & MC13XXX_USE_ADC) if (mc13xxx->flags & MC13XXX_USE_ADC)
mc13xxx_add_subdevice(mc13xxx, "%s-adc"); mc13xxx_add_subdevice(mc13xxx, "%s-adc");
if (mc13xxx->flags & MC13XXX_USE_CODEC) if (mc13xxx->flags & MC13XXX_USE_CODEC) {
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-codec", if (pdata)
pdata->codec, sizeof(*pdata->codec)); mc13xxx_add_subdevice_pdata(mc13xxx, "%s-codec",
pdata->codec, sizeof(*pdata->codec));
else
mc13xxx_add_subdevice(mc13xxx, "%s-codec");
}
if (mc13xxx->flags & MC13XXX_USE_RTC) if (mc13xxx->flags & MC13XXX_USE_RTC)
mc13xxx_add_subdevice(mc13xxx, "%s-rtc"); mc13xxx_add_subdevice(mc13xxx, "%s-rtc");

View file

@ -20,6 +20,7 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <linux/of.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#define DRV_NAME "hdmi-audio-codec" #define DRV_NAME "hdmi-audio-codec"

View file

@ -11,10 +11,12 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/acpi.h>
#include <sound/jack.h> #include <sound/jack.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
@ -1674,6 +1676,7 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
M98090_REG_CLOCK_RATIO_NI_LSB, 0x00); M98090_REG_CLOCK_RATIO_NI_LSB, 0x00);
snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE, snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
M98090_USE_M1_MASK, 0); M98090_USE_M1_MASK, 0);
max98090->master = false;
break; break;
case SND_SOC_DAIFMT_CBM_CFM: case SND_SOC_DAIFMT_CBM_CFM:
/* Set to master mode */ /* Set to master mode */
@ -1690,6 +1693,7 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
regval |= M98090_MAS_MASK | regval |= M98090_MAS_MASK |
M98090_BSEL_32; M98090_BSEL_32;
} }
max98090->master = true;
break; break;
case SND_SOC_DAIFMT_CBS_CFM: case SND_SOC_DAIFMT_CBS_CFM:
case SND_SOC_DAIFMT_CBM_CFS: case SND_SOC_DAIFMT_CBM_CFS:
@ -1793,13 +1797,6 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec,
switch (level) { switch (level) {
case SND_SOC_BIAS_ON: case SND_SOC_BIAS_ON:
if (max98090->jack_state == M98090_JACK_STATE_HEADSET) {
/*
* Set to normal bias level.
*/
snd_soc_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE,
M98090_MBVSEL_MASK, M98090_MBVSEL_2V8);
}
break; break;
case SND_SOC_BIAS_PREPARE: case SND_SOC_BIAS_PREPARE:
@ -1873,7 +1870,8 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
return -EINVAL; return -EINVAL;
} }
max98090_configure_bclk(codec); if (max98090->master)
max98090_configure_bclk(codec);
cdata->rate = max98090->lrclk; cdata->rate = max98090->lrclk;
@ -1952,8 +1950,6 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
max98090->sysclk = freq; max98090->sysclk = freq;
max98090_configure_bclk(codec);
return 0; return 0;
} }
@ -2225,6 +2221,7 @@ static int max98090_probe(struct snd_soc_codec *codec)
/* Initialize private data */ /* Initialize private data */
max98090->sysclk = (unsigned)-1; max98090->sysclk = (unsigned)-1;
max98090->master = false;
cdata = &max98090->dai[0]; cdata = &max98090->dai[0];
cdata->rate = (unsigned)-1; cdata->rate = (unsigned)-1;
@ -2294,6 +2291,9 @@ static int max98090_probe(struct snd_soc_codec *codec)
snd_soc_write(codec, M98090_REG_BIAS_CONTROL, snd_soc_write(codec, M98090_REG_BIAS_CONTROL,
M98090_VCM_MODE_MASK); M98090_VCM_MODE_MASK);
snd_soc_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE,
M98090_MBVSEL_MASK, M98090_MBVSEL_2V8);
max98090_handle_pdata(codec); max98090_handle_pdata(codec);
max98090_add_widgets(codec); max98090_add_widgets(codec);
@ -2330,9 +2330,11 @@ static const struct regmap_config max98090_regmap = {
}; };
static int max98090_i2c_probe(struct i2c_client *i2c, static int max98090_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id) const struct i2c_device_id *i2c_id)
{ {
struct max98090_priv *max98090; struct max98090_priv *max98090;
const struct acpi_device_id *acpi_id;
kernel_ulong_t driver_data = 0;
int ret; int ret;
pr_debug("max98090_i2c_probe\n"); pr_debug("max98090_i2c_probe\n");
@ -2342,7 +2344,19 @@ static int max98090_i2c_probe(struct i2c_client *i2c,
if (max98090 == NULL) if (max98090 == NULL)
return -ENOMEM; return -ENOMEM;
max98090->devtype = id->driver_data; if (ACPI_HANDLE(&i2c->dev)) {
acpi_id = acpi_match_device(i2c->dev.driver->acpi_match_table,
&i2c->dev);
if (!acpi_id) {
dev_err(&i2c->dev, "No driver data\n");
return -EINVAL;
}
driver_data = acpi_id->driver_data;
} else if (i2c_id) {
driver_data = i2c_id->driver_data;
}
max98090->devtype = driver_data;
i2c_set_clientdata(i2c, max98090); i2c_set_clientdata(i2c, max98090);
max98090->pdata = i2c->dev.platform_data; max98090->pdata = i2c->dev.platform_data;
max98090->irq = i2c->irq; max98090->irq = i2c->irq;
@ -2433,12 +2447,21 @@ static const struct of_device_id max98090_of_match[] = {
}; };
MODULE_DEVICE_TABLE(of, max98090_of_match); MODULE_DEVICE_TABLE(of, max98090_of_match);
#ifdef CONFIG_ACPI
static struct acpi_device_id max98090_acpi_match[] = {
{ "193C9890", MAX98090 },
{ }
};
MODULE_DEVICE_TABLE(acpi, max98090_acpi_match);
#endif
static struct i2c_driver max98090_i2c_driver = { static struct i2c_driver max98090_i2c_driver = {
.driver = { .driver = {
.name = "max98090", .name = "max98090",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = &max98090_pm, .pm = &max98090_pm,
.of_match_table = of_match_ptr(max98090_of_match), .of_match_table = of_match_ptr(max98090_of_match),
.acpi_match_table = ACPI_PTR(max98090_acpi_match),
}, },
.probe = max98090_i2c_probe, .probe = max98090_i2c_probe,
.remove = max98090_i2c_remove, .remove = max98090_i2c_remove,

View file

@ -1540,6 +1540,7 @@ struct max98090_priv {
unsigned int pa2en; unsigned int pa2en;
unsigned int extmic_mux; unsigned int extmic_mux;
unsigned int sidetone; unsigned int sidetone;
bool master;
}; };
int max98090_mic_detect(struct snd_soc_codec *codec, int max98090_mic_detect(struct snd_soc_codec *codec,

View file

@ -2399,10 +2399,17 @@ static const struct i2c_device_id max98095_i2c_id[] = {
}; };
MODULE_DEVICE_TABLE(i2c, max98095_i2c_id); MODULE_DEVICE_TABLE(i2c, max98095_i2c_id);
static const struct of_device_id max98095_of_match[] = {
{ .compatible = "maxim,max98095", },
{ }
};
MODULE_DEVICE_TABLE(of, max98095_of_match);
static struct i2c_driver max98095_i2c_driver = { static struct i2c_driver max98095_i2c_driver = {
.driver = { .driver = {
.name = "max98095", .name = "max98095",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.of_match_table = of_match_ptr(max98095_of_match),
}, },
.probe = max98095_i2c_probe, .probe = max98095_i2c_probe,
.remove = max98095_i2c_remove, .remove = max98095_i2c_remove,

View file

@ -22,6 +22,7 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/device.h> #include <linux/device.h>
#include <linux/of.h>
#include <linux/mfd/mc13xxx.h> #include <linux/mfd/mc13xxx.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <sound/core.h> #include <sound/core.h>
@ -748,6 +749,7 @@ static int __init mc13783_codec_probe(struct platform_device *pdev)
{ {
struct mc13783_priv *priv; struct mc13783_priv *priv;
struct mc13xxx_codec_platform_data *pdata = pdev->dev.platform_data; struct mc13xxx_codec_platform_data *pdata = pdev->dev.platform_data;
struct device_node *np;
int ret; int ret;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL); priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
@ -758,7 +760,17 @@ static int __init mc13783_codec_probe(struct platform_device *pdev)
priv->adc_ssi_port = pdata->adc_ssi_port; priv->adc_ssi_port = pdata->adc_ssi_port;
priv->dac_ssi_port = pdata->dac_ssi_port; priv->dac_ssi_port = pdata->dac_ssi_port;
} else { } else {
return -ENOSYS; np = of_get_child_by_name(pdev->dev.parent->of_node, "codec");
if (!np)
return -ENOSYS;
ret = of_property_read_u32(np, "adc-port", &priv->adc_ssi_port);
if (ret)
return ret;
ret = of_property_read_u32(np, "dac-port", &priv->dac_ssi_port);
if (ret)
return ret;
} }
dev_set_drvdata(&pdev->dev, priv); dev_set_drvdata(&pdev->dev, priv);

View file

@ -18,6 +18,7 @@
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <linux/of.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/platform_device.h> #include <linux/platform_device.h>
#include <linux/spi/spi.h> #include <linux/spi/spi.h>

View file

@ -13,6 +13,7 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h>
#include <linux/regmap.h> #include <linux/regmap.h>
#include <sound/soc.h> #include <sound/soc.h>

View file

@ -28,6 +28,7 @@
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/gpio.h> #include <linux/gpio.h>
#include <linux/regulator/consumer.h> #include <linux/regulator/consumer.h>
#include <linux/of.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <sound/core.h> #include <sound/core.h>

View file

@ -30,6 +30,7 @@
#include <sound/tpa6130a2-plat.h> #include <sound/tpa6130a2-plat.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/tlv.h> #include <sound/tlv.h>
#include <linux/of.h>
#include <linux/of_gpio.h> #include <linux/of_gpio.h>
#include "tpa6130a2.h" #include "tpa6130a2.h"

View file

@ -2,7 +2,7 @@
snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o
snd-soc-sst-acpi-objs := sst-acpi.o snd-soc-sst-acpi-objs := sst-acpi.o
snd-soc-sst-mfld-platform-objs := sst-mfld-platform.o snd-soc-sst-mfld-platform-objs := sst-mfld-platform-pcm.o sst-mfld-platform-compress.o
snd-soc-mfld-machine-objs := mfld_machine.o snd-soc-mfld-machine-objs := mfld_machine.o
obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o

View file

@ -111,27 +111,13 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = {
{ {
.name = "Baytrail Audio", .name = "Baytrail Audio",
.stream_name = "Audio", .stream_name = "Audio",
.cpu_dai_name = "Front-cpu-dai", .cpu_dai_name = "baytrail-pcm-audio",
.codec_dai_name = "rt5640-aif1", .codec_dai_name = "rt5640-aif1",
.codec_name = "i2c-10EC5640:00", .codec_name = "i2c-10EC5640:00",
.platform_name = "baytrail-pcm-audio", .platform_name = "baytrail-pcm-audio",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS, SND_SOC_DAIFMT_CBS_CFS,
.init = byt_rt5640_init, .init = byt_rt5640_init,
.ignore_suspend = 1,
.ops = &byt_rt5640_ops,
},
{
.name = "Baytrail Voice",
.stream_name = "Voice",
.cpu_dai_name = "Mic1-cpu-dai",
.codec_dai_name = "rt5640-aif1",
.codec_name = "i2c-10EC5640:00",
.platform_name = "baytrail-pcm-audio",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.init = NULL,
.ignore_suspend = 1,
.ops = &byt_rt5640_ops, .ops = &byt_rt5640_ops,
}, },
}; };
@ -146,6 +132,17 @@ static struct snd_soc_card byt_rt5640_card = {
.num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map), .num_dapm_routes = ARRAY_SIZE(byt_rt5640_audio_map),
}; };
#ifdef CONFIG_PM_SLEEP
static const struct dev_pm_ops byt_rt5640_pm_ops = {
.suspend = snd_soc_suspend,
.resume = snd_soc_resume,
};
#define BYT_RT5640_PM_OPS (&byt_rt5640_pm_ops)
#else
#define BYT_RT5640_PM_OPS NULL
#endif
static int byt_rt5640_probe(struct platform_device *pdev) static int byt_rt5640_probe(struct platform_device *pdev)
{ {
struct snd_soc_card *card = &byt_rt5640_card; struct snd_soc_card *card = &byt_rt5640_card;
@ -171,6 +168,7 @@ static struct platform_driver byt_rt5640_audio = {
.driver = { .driver = {
.name = "byt-rt5640", .name = "byt-rt5640",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = BYT_RT5640_PM_OPS,
}, },
}; };
module_platform_driver(byt_rt5640_audio) module_platform_driver(byt_rt5640_audio)

View file

@ -214,6 +214,13 @@ static void sst_byt_boot(struct sst_dsp *sst)
{ {
int tries = 10; int tries = 10;
/*
* save the physical address of extended firmware block in the first
* 4 bytes of the mailbox
*/
memcpy_toio(sst->addr.lpe + SST_BYT_MAILBOX_OFFSET,
&sst->pdata->fw_base, sizeof(u32));
/* release stall and wait to unstall */ /* release stall and wait to unstall */
sst_dsp_shim_update_bits64(sst, SST_CSR, SST_BYT_CSR_STALL, 0x0); sst_dsp_shim_update_bits64(sst, SST_CSR, SST_BYT_CSR_STALL, 0x0);
while (tries--) { while (tries--) {
@ -317,13 +324,6 @@ static int sst_byt_init(struct sst_dsp *sst, struct sst_pdata *pdata)
return ret; return ret;
} }
/*
* save the physical address of extended firmware block in the first
* 4 bytes of the mailbox
*/
memcpy_toio(sst->addr.lpe + SST_BYT_MAILBOX_OFFSET,
&pdata->fw_base, sizeof(u32));
ret = dma_coerce_mask_and_coherent(sst->dma_dev, DMA_BIT_MASK(32)); ret = dma_coerce_mask_and_coherent(sst->dma_dev, DMA_BIT_MASK(32));
if (ret) if (ret)
return ret; return ret;

View file

@ -173,6 +173,7 @@ struct sst_byt {
/* boot */ /* boot */
wait_queue_head_t boot_wait; wait_queue_head_t boot_wait;
bool boot_complete; bool boot_complete;
struct sst_fw *fw;
/* IPC messaging */ /* IPC messaging */
struct list_head tx_list; struct list_head tx_list;
@ -299,6 +300,24 @@ static inline void sst_byt_tx_msg_reply_complete(struct sst_byt *byt,
wake_up(&msg->waitq); wake_up(&msg->waitq);
} }
static void sst_byt_drop_all(struct sst_byt *byt)
{
struct ipc_message *msg, *tmp;
unsigned long flags;
/* drop all TX and Rx messages before we stall + reset DSP */
spin_lock_irqsave(&byt->dsp->spinlock, flags);
list_for_each_entry_safe(msg, tmp, &byt->tx_list, list) {
list_move(&msg->list, &byt->empty_list);
}
list_for_each_entry_safe(msg, tmp, &byt->rx_list, list) {
list_move(&msg->list, &byt->empty_list);
}
spin_unlock_irqrestore(&byt->dsp->spinlock, flags);
}
static int sst_byt_tx_wait_done(struct sst_byt *byt, struct ipc_message *msg, static int sst_byt_tx_wait_done(struct sst_byt *byt, struct ipc_message *msg,
void *rx_data) void *rx_data)
{ {
@ -661,36 +680,33 @@ out:
static int sst_byt_stream_operations(struct sst_byt *byt, int type, static int sst_byt_stream_operations(struct sst_byt *byt, int type,
int stream_id, int wait) int stream_id, int wait)
{ {
struct sst_byt_start_stream_params start_stream;
u64 header; u64 header;
void *tx_msg = NULL;
size_t size = 0;
if (type != IPC_IA_START_STREAM) {
header = sst_byt_header(type, 0, false, stream_id);
} else {
start_stream.byte_offset = 0;
header = sst_byt_header(IPC_IA_START_STREAM,
sizeof(start_stream) + sizeof(u32),
true, stream_id);
tx_msg = &start_stream;
size = sizeof(start_stream);
}
header = sst_byt_header(type, 0, false, stream_id);
if (wait) if (wait)
return sst_byt_ipc_tx_msg_wait(byt, header, return sst_byt_ipc_tx_msg_wait(byt, header, NULL, 0, NULL, 0);
tx_msg, size, NULL, 0);
else else
return sst_byt_ipc_tx_msg_nowait(byt, header, tx_msg, size); return sst_byt_ipc_tx_msg_nowait(byt, header, NULL, 0);
} }
/* stream ALSA trigger operations */ /* stream ALSA trigger operations */
int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream) int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream,
u32 start_offset)
{ {
struct sst_byt_start_stream_params start_stream;
void *tx_msg;
size_t size;
u64 header;
int ret; int ret;
ret = sst_byt_stream_operations(byt, IPC_IA_START_STREAM, start_stream.byte_offset = start_offset;
stream->str_id, 0); header = sst_byt_header(IPC_IA_START_STREAM,
sizeof(start_stream) + sizeof(u32),
true, stream->str_id);
tx_msg = &start_stream;
size = sizeof(start_stream);
ret = sst_byt_ipc_tx_msg_nowait(byt, header, tx_msg, size);
if (ret < 0) if (ret < 0)
dev_err(byt->dev, "ipc: error failed to start stream %d\n", dev_err(byt->dev, "ipc: error failed to start stream %d\n",
stream->str_id); stream->str_id);
@ -782,6 +798,73 @@ static struct sst_dsp_device byt_dev = {
.ops = &sst_byt_ops, .ops = &sst_byt_ops,
}; };
int sst_byt_dsp_suspend_noirq(struct device *dev, struct sst_pdata *pdata)
{
struct sst_byt *byt = pdata->dsp;
dev_dbg(byt->dev, "dsp reset\n");
sst_dsp_reset(byt->dsp);
sst_byt_drop_all(byt);
dev_dbg(byt->dev, "dsp in reset\n");
return 0;
}
EXPORT_SYMBOL_GPL(sst_byt_dsp_suspend_noirq);
int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata)
{
struct sst_byt *byt = pdata->dsp;
dev_dbg(byt->dev, "free all blocks and unload fw\n");
sst_fw_unload(byt->fw);
return 0;
}
EXPORT_SYMBOL_GPL(sst_byt_dsp_suspend_late);
int sst_byt_dsp_boot(struct device *dev, struct sst_pdata *pdata)
{
struct sst_byt *byt = pdata->dsp;
int ret;
dev_dbg(byt->dev, "reload dsp fw\n");
sst_dsp_reset(byt->dsp);
ret = sst_fw_reload(byt->fw);
if (ret < 0) {
dev_err(dev, "error: failed to reload firmware\n");
return ret;
}
/* wait for DSP boot completion */
byt->boot_complete = false;
sst_dsp_boot(byt->dsp);
dev_dbg(byt->dev, "dsp booting...\n");
return 0;
}
EXPORT_SYMBOL_GPL(sst_byt_dsp_boot);
int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata)
{
struct sst_byt *byt = pdata->dsp;
int err;
dev_dbg(byt->dev, "wait for dsp reboot\n");
err = wait_event_timeout(byt->boot_wait, byt->boot_complete,
msecs_to_jiffies(IPC_BOOT_MSECS));
if (err == 0) {
dev_err(byt->dev, "ipc: error DSP boot timeout\n");
return -EIO;
}
dev_dbg(byt->dev, "dsp rebooted\n");
return 0;
}
EXPORT_SYMBOL_GPL(sst_byt_dsp_wait_for_ready);
int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
{ {
struct sst_byt *byt; struct sst_byt *byt;
@ -848,6 +931,7 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
} }
pdata->dsp = byt; pdata->dsp = byt;
byt->fw = byt_sst_fw;
return 0; return 0;

View file

@ -53,7 +53,8 @@ int sst_byt_stream_commit(struct sst_byt *byt, struct sst_byt_stream *stream);
int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream); int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream);
/* stream ALSA trigger operations */ /* stream ALSA trigger operations */
int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream); int sst_byt_stream_start(struct sst_byt *byt, struct sst_byt_stream *stream,
u32 start_offset);
int sst_byt_stream_stop(struct sst_byt *byt, struct sst_byt_stream *stream); int sst_byt_stream_stop(struct sst_byt *byt, struct sst_byt_stream *stream);
int sst_byt_stream_pause(struct sst_byt *byt, struct sst_byt_stream *stream); int sst_byt_stream_pause(struct sst_byt *byt, struct sst_byt_stream *stream);
int sst_byt_stream_resume(struct sst_byt *byt, struct sst_byt_stream *stream); int sst_byt_stream_resume(struct sst_byt *byt, struct sst_byt_stream *stream);
@ -65,5 +66,9 @@ int sst_byt_get_dsp_position(struct sst_byt *byt,
int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata); int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata);
void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata); void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata);
struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt); struct sst_dsp *sst_byt_get_dsp(struct sst_byt *byt);
int sst_byt_dsp_suspend_noirq(struct device *dev, struct sst_pdata *pdata);
int sst_byt_dsp_suspend_late(struct device *dev, struct sst_pdata *pdata);
int sst_byt_dsp_boot(struct device *dev, struct sst_pdata *pdata);
int sst_byt_dsp_wait_for_ready(struct device *dev, struct sst_pdata *pdata);
#endif #endif

View file

@ -45,6 +45,11 @@ struct sst_byt_pcm_data {
struct sst_byt_stream *stream; struct sst_byt_stream *stream;
struct snd_pcm_substream *substream; struct snd_pcm_substream *substream;
struct mutex mutex; struct mutex mutex;
/* latest DSP DMA hw pointer */
u32 hw_ptr;
struct work_struct work;
}; };
/* private data for the driver */ /* private data for the driver */
@ -63,7 +68,7 @@ static int sst_byt_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct sst_byt_priv_data *pdata = struct sst_byt_priv_data *pdata =
snd_soc_platform_get_drvdata(rtd->platform); snd_soc_platform_get_drvdata(rtd->platform);
struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
struct sst_byt *byt = pdata->byt; struct sst_byt *byt = pdata->byt;
u32 rate, bits; u32 rate, bits;
u8 channels; u8 channels;
@ -130,21 +135,56 @@ static int sst_byt_pcm_hw_free(struct snd_pcm_substream *substream)
return 0; return 0;
} }
static int sst_byt_pcm_restore_stream_context(struct snd_pcm_substream *substream)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct sst_byt_priv_data *pdata =
snd_soc_platform_get_drvdata(rtd->platform);
struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
struct sst_byt *byt = pdata->byt;
int ret;
/* commit stream using existing stream params */
ret = sst_byt_stream_commit(byt, pcm_data->stream);
if (ret < 0) {
dev_err(rtd->dev, "PCM: failed stream commit %d\n", ret);
return ret;
}
sst_byt_stream_start(byt, pcm_data->stream, pcm_data->hw_ptr);
dev_dbg(rtd->dev, "stream context restored at offset %d\n",
pcm_data->hw_ptr);
return 0;
}
static void sst_byt_pcm_work(struct work_struct *work)
{
struct sst_byt_pcm_data *pcm_data =
container_of(work, struct sst_byt_pcm_data, work);
if (snd_pcm_running(pcm_data->substream))
sst_byt_pcm_restore_stream_context(pcm_data->substream);
}
static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd) static int sst_byt_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{ {
struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct sst_byt_priv_data *pdata = struct sst_byt_priv_data *pdata =
snd_soc_platform_get_drvdata(rtd->platform); snd_soc_platform_get_drvdata(rtd->platform);
struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
struct sst_byt *byt = pdata->byt; struct sst_byt *byt = pdata->byt;
dev_dbg(rtd->dev, "PCM: trigger %d\n", cmd); dev_dbg(rtd->dev, "PCM: trigger %d\n", cmd);
switch (cmd) { switch (cmd) {
case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_START:
sst_byt_stream_start(byt, pcm_data->stream); sst_byt_stream_start(byt, pcm_data->stream, 0);
break; break;
case SNDRV_PCM_TRIGGER_RESUME: case SNDRV_PCM_TRIGGER_RESUME:
schedule_work(&pcm_data->work);
break;
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
sst_byt_stream_resume(byt, pcm_data->stream); sst_byt_stream_resume(byt, pcm_data->stream);
break; break;
@ -168,13 +208,19 @@ static u32 byt_notify_pointer(struct sst_byt_stream *stream, void *data)
struct snd_pcm_substream *substream = pcm_data->substream; struct snd_pcm_substream *substream = pcm_data->substream;
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data;
u32 pos; struct sst_byt_priv_data *pdata =
snd_soc_platform_get_drvdata(rtd->platform);
struct sst_byt *byt = pdata->byt;
u32 pos, hw_pos;
hw_pos = sst_byt_get_dsp_position(byt, pcm_data->stream,
snd_pcm_lib_buffer_bytes(substream));
pcm_data->hw_ptr = hw_pos;
pos = frames_to_bytes(runtime, pos = frames_to_bytes(runtime,
(runtime->control->appl_ptr % (runtime->control->appl_ptr %
runtime->buffer_size)); runtime->buffer_size));
dev_dbg(rtd->dev, "PCM: App pointer %d bytes\n", pos); dev_dbg(rtd->dev, "PCM: App/DMA pointer %u/%u bytes\n", pos, hw_pos);
snd_pcm_period_elapsed(substream); snd_pcm_period_elapsed(substream);
return pos; return pos;
@ -186,18 +232,11 @@ static snd_pcm_uframes_t sst_byt_pcm_pointer(struct snd_pcm_substream *substream
struct snd_pcm_runtime *runtime = substream->runtime; struct snd_pcm_runtime *runtime = substream->runtime;
struct sst_byt_priv_data *pdata = struct sst_byt_priv_data *pdata =
snd_soc_platform_get_drvdata(rtd->platform); snd_soc_platform_get_drvdata(rtd->platform);
struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
struct sst_byt *byt = pdata->byt;
snd_pcm_uframes_t offset;
int pos;
pos = sst_byt_get_dsp_position(byt, pcm_data->stream, dev_dbg(rtd->dev, "PCM: DMA pointer %u bytes\n", pcm_data->hw_ptr);
snd_pcm_lib_buffer_bytes(substream));
offset = bytes_to_frames(runtime, pos);
dev_dbg(rtd->dev, "PCM: DMA pointer %zu bytes\n", return bytes_to_frames(runtime, pcm_data->hw_ptr);
frames_to_bytes(runtime, (u32)offset));
return offset;
} }
static int sst_byt_pcm_open(struct snd_pcm_substream *substream) static int sst_byt_pcm_open(struct snd_pcm_substream *substream)
@ -205,20 +244,18 @@ static int sst_byt_pcm_open(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct sst_byt_priv_data *pdata = struct sst_byt_priv_data *pdata =
snd_soc_platform_get_drvdata(rtd->platform); snd_soc_platform_get_drvdata(rtd->platform);
struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
struct sst_byt *byt = pdata->byt; struct sst_byt *byt = pdata->byt;
dev_dbg(rtd->dev, "PCM: open\n"); dev_dbg(rtd->dev, "PCM: open\n");
pcm_data = &pdata->pcm[rtd->cpu_dai->id];
mutex_lock(&pcm_data->mutex); mutex_lock(&pcm_data->mutex);
snd_soc_pcm_set_drvdata(rtd, pcm_data);
pcm_data->substream = substream; pcm_data->substream = substream;
snd_soc_set_runtime_hwparams(substream, &sst_byt_pcm_hardware); snd_soc_set_runtime_hwparams(substream, &sst_byt_pcm_hardware);
pcm_data->stream = sst_byt_stream_new(byt, rtd->cpu_dai->id + 1, pcm_data->stream = sst_byt_stream_new(byt, substream->stream + 1,
byt_notify_pointer, pcm_data); byt_notify_pointer, pcm_data);
if (pcm_data->stream == NULL) { if (pcm_data->stream == NULL) {
dev_err(rtd->dev, "failed to create stream\n"); dev_err(rtd->dev, "failed to create stream\n");
@ -235,12 +272,13 @@ static int sst_byt_pcm_close(struct snd_pcm_substream *substream)
struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct sst_byt_priv_data *pdata = struct sst_byt_priv_data *pdata =
snd_soc_platform_get_drvdata(rtd->platform); snd_soc_platform_get_drvdata(rtd->platform);
struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd); struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
struct sst_byt *byt = pdata->byt; struct sst_byt *byt = pdata->byt;
int ret; int ret;
dev_dbg(rtd->dev, "PCM: close\n"); dev_dbg(rtd->dev, "PCM: close\n");
cancel_work_sync(&pcm_data->work);
mutex_lock(&pcm_data->mutex); mutex_lock(&pcm_data->mutex);
ret = sst_byt_stream_free(byt, pcm_data->stream); ret = sst_byt_stream_free(byt, pcm_data->stream);
if (ret < 0) { if (ret < 0) {
@ -283,18 +321,16 @@ static int sst_byt_pcm_new(struct snd_soc_pcm_runtime *rtd)
{ {
struct snd_pcm *pcm = rtd->pcm; struct snd_pcm *pcm = rtd->pcm;
size_t size; size_t size;
struct snd_soc_platform *platform = rtd->platform;
struct sst_pdata *pdata = dev_get_platdata(platform->dev);
int ret = 0; int ret = 0;
ret = dma_coerce_mask_and_coherent(rtd->card->dev, DMA_BIT_MASK(32));
if (ret)
return ret;
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream || if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream ||
pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
size = sst_byt_pcm_hardware.buffer_bytes_max; size = sst_byt_pcm_hardware.buffer_bytes_max;
ret = snd_pcm_lib_preallocate_pages_for_all(pcm, ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_DEV, SNDRV_DMA_TYPE_DEV,
rtd->card->dev, pdata->dma_dev,
size, size); size, size);
if (ret) { if (ret) {
dev_err(rtd->dev, "dma buffer allocation failed %d\n", dev_err(rtd->dev, "dma buffer allocation failed %d\n",
@ -308,7 +344,7 @@ static int sst_byt_pcm_new(struct snd_soc_pcm_runtime *rtd)
static struct snd_soc_dai_driver byt_dais[] = { static struct snd_soc_dai_driver byt_dais[] = {
{ {
.name = "Front-cpu-dai", .name = "Baytrail PCM",
.playback = { .playback = {
.stream_name = "System Playback", .stream_name = "System Playback",
.channels_min = 2, .channels_min = 2,
@ -317,9 +353,6 @@ static struct snd_soc_dai_driver byt_dais[] = {
.formats = SNDRV_PCM_FMTBIT_S24_3LE | .formats = SNDRV_PCM_FMTBIT_S24_3LE |
SNDRV_PCM_FMTBIT_S16_LE, SNDRV_PCM_FMTBIT_S16_LE,
}, },
},
{
.name = "Mic1-cpu-dai",
.capture = { .capture = {
.stream_name = "Analog Capture", .stream_name = "Analog Capture",
.channels_min = 2, .channels_min = 2,
@ -344,8 +377,10 @@ static int sst_byt_pcm_probe(struct snd_soc_platform *platform)
priv_data->byt = plat_data->dsp; priv_data->byt = plat_data->dsp;
snd_soc_platform_set_drvdata(platform, priv_data); snd_soc_platform_set_drvdata(platform, priv_data);
for (i = 0; i < ARRAY_SIZE(byt_dais); i++) for (i = 0; i < BYT_PCM_COUNT; i++) {
mutex_init(&priv_data->pcm[i].mutex); mutex_init(&priv_data->pcm[i].mutex);
INIT_WORK(&priv_data->pcm[i].work, sst_byt_pcm_work);
}
return 0; return 0;
} }
@ -367,6 +402,72 @@ static const struct snd_soc_component_driver byt_dai_component = {
.name = "byt-dai", .name = "byt-dai",
}; };
#ifdef CONFIG_PM
static int sst_byt_pcm_dev_suspend_noirq(struct device *dev)
{
struct sst_pdata *sst_pdata = dev_get_platdata(dev);
int ret;
dev_dbg(dev, "suspending noirq\n");
/* at this point all streams will be stopped and context saved */
ret = sst_byt_dsp_suspend_noirq(dev, sst_pdata);
if (ret < 0) {
dev_err(dev, "failed to suspend %d\n", ret);
return ret;
}
return ret;
}
static int sst_byt_pcm_dev_suspend_late(struct device *dev)
{
struct sst_pdata *sst_pdata = dev_get_platdata(dev);
int ret;
dev_dbg(dev, "suspending late\n");
ret = sst_byt_dsp_suspend_late(dev, sst_pdata);
if (ret < 0) {
dev_err(dev, "failed to suspend %d\n", ret);
return ret;
}
return ret;
}
static int sst_byt_pcm_dev_resume_early(struct device *dev)
{
struct sst_pdata *sst_pdata = dev_get_platdata(dev);
dev_dbg(dev, "resume early\n");
/* load fw and boot DSP */
return sst_byt_dsp_boot(dev, sst_pdata);
}
static int sst_byt_pcm_dev_resume(struct device *dev)
{
struct sst_pdata *sst_pdata = dev_get_platdata(dev);
dev_dbg(dev, "resume\n");
/* wait for FW to finish booting */
return sst_byt_dsp_wait_for_ready(dev, sst_pdata);
}
static const struct dev_pm_ops sst_byt_pm_ops = {
.suspend_noirq = sst_byt_pcm_dev_suspend_noirq,
.suspend_late = sst_byt_pcm_dev_suspend_late,
.resume_early = sst_byt_pcm_dev_resume_early,
.resume = sst_byt_pcm_dev_resume,
};
#define SST_BYT_PM_OPS (&sst_byt_pm_ops)
#else
#define SST_BYT_PM_OPS NULL
#endif
static int sst_byt_pcm_dev_probe(struct platform_device *pdev) static int sst_byt_pcm_dev_probe(struct platform_device *pdev)
{ {
struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev); struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
@ -409,6 +510,7 @@ static struct platform_driver sst_byt_pcm_driver = {
.driver = { .driver = {
.name = "baytrail-pcm-audio", .name = "baytrail-pcm-audio",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.pm = SST_BYT_PM_OPS,
}, },
.probe = sst_byt_pcm_dev_probe, .probe = sst_byt_pcm_dev_probe,

View file

@ -284,6 +284,8 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
const struct firmware *fw, void *private); const struct firmware *fw, void *private);
void sst_fw_free(struct sst_fw *sst_fw); void sst_fw_free(struct sst_fw *sst_fw);
void sst_fw_free_all(struct sst_dsp *dsp); void sst_fw_free_all(struct sst_dsp *dsp);
int sst_fw_reload(struct sst_fw *sst_fw);
void sst_fw_unload(struct sst_fw *sst_fw);
/* Create/Free firmware modules */ /* Create/Free firmware modules */
struct sst_module *sst_module_new(struct sst_fw *sst_fw, struct sst_module *sst_module_new(struct sst_fw *sst_fw,

View file

@ -30,6 +30,8 @@
#include "sst-dsp.h" #include "sst-dsp.h"
#include "sst-dsp-priv.h" #include "sst-dsp-priv.h"
static void block_module_remove(struct sst_module *module);
static void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes) static void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes)
{ {
u32 i; u32 i;
@ -91,6 +93,42 @@ parse_err:
} }
EXPORT_SYMBOL_GPL(sst_fw_new); EXPORT_SYMBOL_GPL(sst_fw_new);
int sst_fw_reload(struct sst_fw *sst_fw)
{
struct sst_dsp *dsp = sst_fw->dsp;
int ret;
dev_dbg(dsp->dev, "reloading firmware\n");
/* call core specific FW paser to load FW data into DSP */
ret = dsp->ops->parse_fw(sst_fw);
if (ret < 0)
dev_err(dsp->dev, "error: parse fw failed %d\n", ret);
return ret;
}
EXPORT_SYMBOL_GPL(sst_fw_reload);
void sst_fw_unload(struct sst_fw *sst_fw)
{
struct sst_dsp *dsp = sst_fw->dsp;
struct sst_module *module, *tmp;
dev_dbg(dsp->dev, "unloading firmware\n");
mutex_lock(&dsp->mutex);
list_for_each_entry_safe(module, tmp, &dsp->module_list, list) {
if (module->sst_fw == sst_fw) {
block_module_remove(module);
list_del(&module->list);
kfree(module);
}
}
mutex_unlock(&dsp->mutex);
}
EXPORT_SYMBOL_GPL(sst_fw_unload);
/* free single firmware object */ /* free single firmware object */
void sst_fw_free(struct sst_fw *sst_fw) void sst_fw_free(struct sst_fw *sst_fw)
{ {
@ -496,9 +534,7 @@ struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp)
/* calculate required scratch size */ /* calculate required scratch size */
list_for_each_entry(sst_module, &dsp->module_list, list) { list_for_each_entry(sst_module, &dsp->module_list, list) {
if (scratch->s.size > sst_module->s.size) if (scratch->s.size < sst_module->s.size)
scratch->s.size = scratch->s.size;
else
scratch->s.size = sst_module->s.size; scratch->s.size = sst_module->s.size;
} }

View file

@ -16,10 +16,6 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/ */
@ -40,7 +36,6 @@ enum stream_type {
}; };
struct snd_pcm_params { struct snd_pcm_params {
u16 codec; /* codec type */
u8 num_chan; /* 1=Mono, 2=Stereo */ u8 num_chan; /* 1=Mono, 2=Stereo */
u8 pcm_wd_sz; /* 16/24 - bit*/ u8 pcm_wd_sz; /* 16/24 - bit*/
u32 reserved; /* Bitrate in bits per second */ u32 reserved; /* Bitrate in bits per second */
@ -53,7 +48,6 @@ struct snd_pcm_params {
/* MP3 Music Parameters Message */ /* MP3 Music Parameters Message */
struct snd_mp3_params { struct snd_mp3_params {
u16 codec;
u8 num_chan; /* 1=Mono, 2=Stereo */ u8 num_chan; /* 1=Mono, 2=Stereo */
u8 pcm_wd_sz; /* 16/24 - bit*/ u8 pcm_wd_sz; /* 16/24 - bit*/
u8 crc_check; /* crc_check - disable (0) or enable (1) */ u8 crc_check; /* crc_check - disable (0) or enable (1) */
@ -67,7 +61,6 @@ struct snd_mp3_params {
/* AAC Music Parameters Message */ /* AAC Music Parameters Message */
struct snd_aac_params { struct snd_aac_params {
u16 codec;
u8 num_chan; /* 1=Mono, 2=Stereo*/ u8 num_chan; /* 1=Mono, 2=Stereo*/
u8 pcm_wd_sz; /* 16/24 - bit*/ u8 pcm_wd_sz; /* 16/24 - bit*/
u8 bdownsample; /*SBR downsampling 0 - disable 1 -enabled AAC+ only */ u8 bdownsample; /*SBR downsampling 0 - disable 1 -enabled AAC+ only */
@ -81,7 +74,6 @@ struct snd_aac_params {
/* WMA Music Parameters Message */ /* WMA Music Parameters Message */
struct snd_wma_params { struct snd_wma_params {
u16 codec;
u8 num_chan; /* 1=Mono, 2=Stereo */ u8 num_chan; /* 1=Mono, 2=Stereo */
u8 pcm_wd_sz; /* 16/24 - bit*/ u8 pcm_wd_sz; /* 16/24 - bit*/
u32 brate; /* Use the hard coded value. */ u32 brate; /* Use the hard coded value. */

View file

@ -0,0 +1,237 @@
/*
* sst_mfld_platform.c - Intel MID Platform driver
*
* Copyright (C) 2010-2014 Intel Corp
* Author: Vinod Koul <vinod.koul@intel.com>
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
* 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; version 2 of the License.
*
* 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.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/module.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/compress_driver.h>
#include "sst-mfld-platform.h"
/* compress stream operations */
static void sst_compr_fragment_elapsed(void *arg)
{
struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg;
pr_debug("fragment elapsed by driver\n");
if (cstream)
snd_compr_fragment_elapsed(cstream);
}
static void sst_drain_notify(void *arg)
{
struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg;
pr_debug("drain notify by driver\n");
if (cstream)
snd_compr_drain_notify(cstream);
}
static int sst_platform_compr_open(struct snd_compr_stream *cstream)
{
int ret_val = 0;
struct snd_compr_runtime *runtime = cstream->runtime;
struct sst_runtime_stream *stream;
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
if (!stream)
return -ENOMEM;
spin_lock_init(&stream->status_lock);
/* get the sst ops */
if (!sst || !try_module_get(sst->dev->driver->owner)) {
pr_err("no device available to run\n");
ret_val = -ENODEV;
goto out_ops;
}
stream->compr_ops = sst->compr_ops;
stream->id = 0;
sst_set_stream_status(stream, SST_PLATFORM_INIT);
runtime->private_data = stream;
return 0;
out_ops:
kfree(stream);
return ret_val;
}
static int sst_platform_compr_free(struct snd_compr_stream *cstream)
{
struct sst_runtime_stream *stream;
int ret_val = 0, str_id;
stream = cstream->runtime->private_data;
/*need to check*/
str_id = stream->id;
if (str_id)
ret_val = stream->compr_ops->close(str_id);
module_put(sst->dev->driver->owner);
kfree(stream);
pr_debug("%s: %d\n", __func__, ret_val);
return 0;
}
static int sst_platform_compr_set_params(struct snd_compr_stream *cstream,
struct snd_compr_params *params)
{
struct sst_runtime_stream *stream;
int retval;
struct snd_sst_params str_params;
struct sst_compress_cb cb;
stream = cstream->runtime->private_data;
/* construct fw structure for this*/
memset(&str_params, 0, sizeof(str_params));
str_params.ops = STREAM_OPS_PLAYBACK;
str_params.stream_type = SST_STREAM_TYPE_MUSIC;
str_params.device_type = SND_SST_DEVICE_COMPRESS;
switch (params->codec.id) {
case SND_AUDIOCODEC_MP3: {
str_params.codec = SST_CODEC_TYPE_MP3;
str_params.sparams.uc.mp3_params.num_chan = params->codec.ch_in;
str_params.sparams.uc.mp3_params.pcm_wd_sz = 16;
break;
}
case SND_AUDIOCODEC_AAC: {
str_params.codec = SST_CODEC_TYPE_AAC;
str_params.sparams.uc.aac_params.num_chan = params->codec.ch_in;
str_params.sparams.uc.aac_params.pcm_wd_sz = 16;
if (params->codec.format == SND_AUDIOSTREAMFORMAT_MP4ADTS)
str_params.sparams.uc.aac_params.bs_format =
AAC_BIT_STREAM_ADTS;
else if (params->codec.format == SND_AUDIOSTREAMFORMAT_RAW)
str_params.sparams.uc.aac_params.bs_format =
AAC_BIT_STREAM_RAW;
else {
pr_err("Undefined format%d\n", params->codec.format);
return -EINVAL;
}
str_params.sparams.uc.aac_params.externalsr =
params->codec.sample_rate;
break;
}
default:
pr_err("codec not supported, id =%d\n", params->codec.id);
return -EINVAL;
}
str_params.aparams.ring_buf_info[0].addr =
virt_to_phys(cstream->runtime->buffer);
str_params.aparams.ring_buf_info[0].size =
cstream->runtime->buffer_size;
str_params.aparams.sg_count = 1;
str_params.aparams.frag_size = cstream->runtime->fragment_size;
cb.param = cstream;
cb.compr_cb = sst_compr_fragment_elapsed;
cb.drain_cb_param = cstream;
cb.drain_notify = sst_drain_notify;
retval = stream->compr_ops->open(&str_params, &cb);
if (retval < 0) {
pr_err("stream allocation failed %d\n", retval);
return retval;
}
stream->id = retval;
return 0;
}
static int sst_platform_compr_trigger(struct snd_compr_stream *cstream, int cmd)
{
struct sst_runtime_stream *stream =
cstream->runtime->private_data;
return stream->compr_ops->control(cmd, stream->id);
}
static int sst_platform_compr_pointer(struct snd_compr_stream *cstream,
struct snd_compr_tstamp *tstamp)
{
struct sst_runtime_stream *stream;
stream = cstream->runtime->private_data;
stream->compr_ops->tstamp(stream->id, tstamp);
tstamp->byte_offset = tstamp->copied_total %
(u32)cstream->runtime->buffer_size;
pr_debug("calc bytes offset/copied bytes as %d\n", tstamp->byte_offset);
return 0;
}
static int sst_platform_compr_ack(struct snd_compr_stream *cstream,
size_t bytes)
{
struct sst_runtime_stream *stream;
stream = cstream->runtime->private_data;
stream->compr_ops->ack(stream->id, (unsigned long)bytes);
stream->bytes_written += bytes;
return 0;
}
static int sst_platform_compr_get_caps(struct snd_compr_stream *cstream,
struct snd_compr_caps *caps)
{
struct sst_runtime_stream *stream =
cstream->runtime->private_data;
return stream->compr_ops->get_caps(caps);
}
static int sst_platform_compr_get_codec_caps(struct snd_compr_stream *cstream,
struct snd_compr_codec_caps *codec)
{
struct sst_runtime_stream *stream =
cstream->runtime->private_data;
return stream->compr_ops->get_codec_caps(codec);
}
static int sst_platform_compr_set_metadata(struct snd_compr_stream *cstream,
struct snd_compr_metadata *metadata)
{
struct sst_runtime_stream *stream =
cstream->runtime->private_data;
return stream->compr_ops->set_metadata(stream->id, metadata);
}
struct snd_compr_ops sst_platform_compr_ops = {
.open = sst_platform_compr_open,
.free = sst_platform_compr_free,
.set_params = sst_platform_compr_set_params,
.set_metadata = sst_platform_compr_set_metadata,
.trigger = sst_platform_compr_trigger,
.pointer = sst_platform_compr_pointer,
.ack = sst_platform_compr_ack,
.get_caps = sst_platform_compr_get_caps,
.get_codec_caps = sst_platform_compr_get_codec_caps,
};

View file

@ -15,13 +15,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
*
*/ */
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@ -35,8 +29,9 @@
#include <sound/compress_driver.h> #include <sound/compress_driver.h>
#include "sst-mfld-platform.h" #include "sst-mfld-platform.h"
static struct sst_device *sst; struct sst_device *sst;
static DEFINE_MUTEX(sst_lock); static DEFINE_MUTEX(sst_lock);
extern struct snd_compr_ops sst_platform_compr_ops;
int sst_register_dsp(struct sst_device *dev) int sst_register_dsp(struct sst_device *dev)
{ {
@ -115,36 +110,6 @@ static struct snd_soc_dai_driver sst_platform_dai[] = {
.formats = SNDRV_PCM_FMTBIT_S24_LE, .formats = SNDRV_PCM_FMTBIT_S24_LE,
}, },
}, },
{
.name = "Speaker-cpu-dai",
.id = 1,
.playback = {
.channels_min = SST_MONO,
.channels_max = SST_STEREO,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S24_LE,
},
},
{
.name = "Vibra1-cpu-dai",
.id = 2,
.playback = {
.channels_min = SST_MONO,
.channels_max = SST_MONO,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S24_LE,
},
},
{
.name = "Vibra2-cpu-dai",
.id = 3,
.playback = {
.channels_min = SST_MONO,
.channels_max = SST_STEREO,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S24_LE,
},
},
{ {
.name = "Compress-cpu-dai", .name = "Compress-cpu-dai",
.compress_dai = 1, .compress_dai = 1,
@ -157,12 +122,8 @@ static struct snd_soc_dai_driver sst_platform_dai[] = {
}, },
}; };
static const struct snd_soc_component_driver sst_component = {
.name = "sst",
};
/* helper functions */ /* helper functions */
static inline void sst_set_stream_status(struct sst_runtime_stream *stream, void sst_set_stream_status(struct sst_runtime_stream *stream,
int state) int state)
{ {
unsigned long flags; unsigned long flags;
@ -186,7 +147,6 @@ static void sst_fill_pcm_params(struct snd_pcm_substream *substream,
struct sst_pcm_params *param) struct sst_pcm_params *param)
{ {
param->codec = SST_CODEC_TYPE_PCM;
param->num_chan = (u8) substream->runtime->channels; param->num_chan = (u8) substream->runtime->channels;
param->pcm_wd_sz = substream->runtime->sample_bits; param->pcm_wd_sz = substream->runtime->sample_bits;
param->reserved = 0; param->reserved = 0;
@ -471,205 +431,6 @@ static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd)
return retval; return retval;
} }
/* compress stream operations */
static void sst_compr_fragment_elapsed(void *arg)
{
struct snd_compr_stream *cstream = (struct snd_compr_stream *)arg;
pr_debug("fragment elapsed by driver\n");
if (cstream)
snd_compr_fragment_elapsed(cstream);
}
static int sst_platform_compr_open(struct snd_compr_stream *cstream)
{
int ret_val = 0;
struct snd_compr_runtime *runtime = cstream->runtime;
struct sst_runtime_stream *stream;
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
if (!stream)
return -ENOMEM;
spin_lock_init(&stream->status_lock);
/* get the sst ops */
if (!sst || !try_module_get(sst->dev->driver->owner)) {
pr_err("no device available to run\n");
ret_val = -ENODEV;
goto out_ops;
}
stream->compr_ops = sst->compr_ops;
stream->id = 0;
sst_set_stream_status(stream, SST_PLATFORM_INIT);
runtime->private_data = stream;
return 0;
out_ops:
kfree(stream);
return ret_val;
}
static int sst_platform_compr_free(struct snd_compr_stream *cstream)
{
struct sst_runtime_stream *stream;
int ret_val = 0, str_id;
stream = cstream->runtime->private_data;
/*need to check*/
str_id = stream->id;
if (str_id)
ret_val = stream->compr_ops->close(str_id);
module_put(sst->dev->driver->owner);
kfree(stream);
pr_debug("%s: %d\n", __func__, ret_val);
return 0;
}
static int sst_platform_compr_set_params(struct snd_compr_stream *cstream,
struct snd_compr_params *params)
{
struct sst_runtime_stream *stream;
int retval;
struct snd_sst_params str_params;
struct sst_compress_cb cb;
stream = cstream->runtime->private_data;
/* construct fw structure for this*/
memset(&str_params, 0, sizeof(str_params));
str_params.ops = STREAM_OPS_PLAYBACK;
str_params.stream_type = SST_STREAM_TYPE_MUSIC;
str_params.device_type = SND_SST_DEVICE_COMPRESS;
switch (params->codec.id) {
case SND_AUDIOCODEC_MP3: {
str_params.codec = SST_CODEC_TYPE_MP3;
str_params.sparams.uc.mp3_params.codec = SST_CODEC_TYPE_MP3;
str_params.sparams.uc.mp3_params.num_chan = params->codec.ch_in;
str_params.sparams.uc.mp3_params.pcm_wd_sz = 16;
break;
}
case SND_AUDIOCODEC_AAC: {
str_params.codec = SST_CODEC_TYPE_AAC;
str_params.sparams.uc.aac_params.codec = SST_CODEC_TYPE_AAC;
str_params.sparams.uc.aac_params.num_chan = params->codec.ch_in;
str_params.sparams.uc.aac_params.pcm_wd_sz = 16;
if (params->codec.format == SND_AUDIOSTREAMFORMAT_MP4ADTS)
str_params.sparams.uc.aac_params.bs_format =
AAC_BIT_STREAM_ADTS;
else if (params->codec.format == SND_AUDIOSTREAMFORMAT_RAW)
str_params.sparams.uc.aac_params.bs_format =
AAC_BIT_STREAM_RAW;
else {
pr_err("Undefined format%d\n", params->codec.format);
return -EINVAL;
}
str_params.sparams.uc.aac_params.externalsr =
params->codec.sample_rate;
break;
}
default:
pr_err("codec not supported, id =%d\n", params->codec.id);
return -EINVAL;
}
str_params.aparams.ring_buf_info[0].addr =
virt_to_phys(cstream->runtime->buffer);
str_params.aparams.ring_buf_info[0].size =
cstream->runtime->buffer_size;
str_params.aparams.sg_count = 1;
str_params.aparams.frag_size = cstream->runtime->fragment_size;
cb.param = cstream;
cb.compr_cb = sst_compr_fragment_elapsed;
retval = stream->compr_ops->open(&str_params, &cb);
if (retval < 0) {
pr_err("stream allocation failed %d\n", retval);
return retval;
}
stream->id = retval;
return 0;
}
static int sst_platform_compr_trigger(struct snd_compr_stream *cstream, int cmd)
{
struct sst_runtime_stream *stream =
cstream->runtime->private_data;
return stream->compr_ops->control(cmd, stream->id);
}
static int sst_platform_compr_pointer(struct snd_compr_stream *cstream,
struct snd_compr_tstamp *tstamp)
{
struct sst_runtime_stream *stream;
stream = cstream->runtime->private_data;
stream->compr_ops->tstamp(stream->id, tstamp);
tstamp->byte_offset = tstamp->copied_total %
(u32)cstream->runtime->buffer_size;
pr_debug("calc bytes offset/copied bytes as %d\n", tstamp->byte_offset);
return 0;
}
static int sst_platform_compr_ack(struct snd_compr_stream *cstream,
size_t bytes)
{
struct sst_runtime_stream *stream;
stream = cstream->runtime->private_data;
stream->compr_ops->ack(stream->id, (unsigned long)bytes);
stream->bytes_written += bytes;
return 0;
}
static int sst_platform_compr_get_caps(struct snd_compr_stream *cstream,
struct snd_compr_caps *caps)
{
struct sst_runtime_stream *stream =
cstream->runtime->private_data;
return stream->compr_ops->get_caps(caps);
}
static int sst_platform_compr_get_codec_caps(struct snd_compr_stream *cstream,
struct snd_compr_codec_caps *codec)
{
struct sst_runtime_stream *stream =
cstream->runtime->private_data;
return stream->compr_ops->get_codec_caps(codec);
}
static int sst_platform_compr_set_metadata(struct snd_compr_stream *cstream,
struct snd_compr_metadata *metadata)
{
struct sst_runtime_stream *stream =
cstream->runtime->private_data;
return stream->compr_ops->set_metadata(stream->id, metadata);
}
static struct snd_compr_ops sst_platform_compr_ops = {
.open = sst_platform_compr_open,
.free = sst_platform_compr_free,
.set_params = sst_platform_compr_set_params,
.set_metadata = sst_platform_compr_set_metadata,
.trigger = sst_platform_compr_trigger,
.pointer = sst_platform_compr_pointer,
.ack = sst_platform_compr_ack,
.get_caps = sst_platform_compr_get_caps,
.get_codec_caps = sst_platform_compr_get_codec_caps,
};
static struct snd_soc_platform_driver sst_soc_platform_drv = { static struct snd_soc_platform_driver sst_soc_platform_drv = {
.ops = &sst_platform_ops, .ops = &sst_platform_ops,
.compr_ops = &sst_platform_compr_ops, .compr_ops = &sst_platform_compr_ops,
@ -677,6 +438,11 @@ static struct snd_soc_platform_driver sst_soc_platform_drv = {
.pcm_free = sst_pcm_free, .pcm_free = sst_pcm_free,
}; };
static const struct snd_soc_component_driver sst_component = {
.name = "sst",
};
static int sst_platform_probe(struct platform_device *pdev) static int sst_platform_probe(struct platform_device *pdev)
{ {
int ret; int ret;

View file

@ -15,13 +15,7 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details. * General Public License for more details.
* *
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
*
*/ */
#ifndef __SST_PLATFORMDRV_H__ #ifndef __SST_PLATFORMDRV_H__
@ -29,6 +23,8 @@
#include "sst-mfld-dsp.h" #include "sst-mfld-dsp.h"
extern struct sst_device *sst;
#define SST_MONO 1 #define SST_MONO 1
#define SST_STEREO 2 #define SST_STEREO 2
#define SST_MAX_CAP 5 #define SST_MAX_CAP 5
@ -108,6 +104,8 @@ struct sst_stream_params {
struct sst_compress_cb { struct sst_compress_cb {
void *param; void *param;
void (*compr_cb)(void *param); void (*compr_cb)(void *param);
void *drain_cb_param;
void (*drain_notify)(void *param);
}; };
struct compress_sst_ops { struct compress_sst_ops {
@ -148,6 +146,7 @@ struct sst_device {
struct compress_sst_ops *compr_ops; struct compress_sst_ops *compr_ops;
}; };
void sst_set_stream_status(struct sst_runtime_stream *stream, int state);
int sst_register_dsp(struct sst_device *sst); int sst_register_dsp(struct sst_device *sst);
int sst_unregister_dsp(struct sst_device *sst); int sst_unregister_dsp(struct sst_device *sst);
#endif #endif

View file

@ -1,24 +1,29 @@
config SND_JZ4740_SOC config SND_JZ4740_SOC
tristate "SoC Audio for Ingenic JZ4740 SoC" tristate "SoC Audio for Ingenic JZ4740 SoC"
depends on MACH_JZ4740 && SND_SOC depends on MACH_JZ4740 || COMPILE_TEST
select SND_SOC_GENERIC_DMAENGINE_PCM select SND_SOC_GENERIC_DMAENGINE_PCM
help help
Say Y or M if you want to add support for codecs attached to Say Y or M if you want to add support for codecs attached to
the JZ4740 I2S interface. You will also need to select the audio the JZ4740 I2S interface. You will also need to select the audio
interfaces to support below. interfaces to support below.
if SND_JZ4740_SOC
config SND_JZ4740_SOC_I2S config SND_JZ4740_SOC_I2S
depends on SND_JZ4740_SOC
tristate "SoC Audio (I2S protocol) for Ingenic JZ4740 SoC" tristate "SoC Audio (I2S protocol) for Ingenic JZ4740 SoC"
depends on HAS_IOMEM
help help
Say Y if you want to use I2S protocol and I2S codec on Ingenic JZ4740 Say Y if you want to use I2S protocol and I2S codec on Ingenic JZ4740
based boards. based boards.
config SND_JZ4740_SOC_QI_LB60 config SND_JZ4740_SOC_QI_LB60
tristate "SoC Audio support for Qi LB60" tristate "SoC Audio support for Qi LB60"
depends on SND_JZ4740_SOC && JZ4740_QI_LB60 depends on HAS_IOMEM
depends on JZ4740_QI_LB60 || COMPILE_TEST
select SND_JZ4740_SOC_I2S select SND_JZ4740_SOC_I2S
select SND_SOC_JZ4740_CODEC select SND_SOC_JZ4740_CODEC
help help
Say Y if you want to add support for ASoC audio on the Qi LB60 board Say Y if you want to add support for ASoC audio on the Qi LB60 board
a.k.a Qi Ben NanoNote. a.k.a Qi Ben NanoNote.
endif

View file

@ -31,10 +31,11 @@
#include <sound/initval.h> #include <sound/initval.h>
#include <sound/dmaengine_pcm.h> #include <sound/dmaengine_pcm.h>
#include <asm/mach-jz4740/dma.h>
#include "jz4740-i2s.h" #include "jz4740-i2s.h"
#define JZ4740_DMA_TYPE_AIC_TRANSMIT 24
#define JZ4740_DMA_TYPE_AIC_RECEIVE 25
#define JZ_REG_AIC_CONF 0x00 #define JZ_REG_AIC_CONF 0x00
#define JZ_REG_AIC_CTRL 0x04 #define JZ_REG_AIC_CTRL 0x04
#define JZ_REG_AIC_I2S_FMT 0x10 #define JZ_REG_AIC_I2S_FMT 0x10

View file

@ -19,18 +19,21 @@
#include <sound/core.h> #include <sound/core.h>
#include <sound/pcm.h> #include <sound/pcm.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <linux/gpio.h> #include <linux/gpio/consumer.h>
#define QI_LB60_SND_GPIO JZ_GPIO_PORTB(29) struct qi_lb60 {
#define QI_LB60_AMP_GPIO JZ_GPIO_PORTD(4) struct gpio_desc *snd_gpio;
struct gpio_desc *amp_gpio;
};
static int qi_lb60_spk_event(struct snd_soc_dapm_widget *widget, static int qi_lb60_spk_event(struct snd_soc_dapm_widget *widget,
struct snd_kcontrol *ctrl, int event) struct snd_kcontrol *ctrl, int event)
{ {
struct qi_lb60 *qi_lb60 = snd_soc_card_get_drvdata(widget->dapm->card);
int on = !SND_SOC_DAPM_EVENT_OFF(event); int on = !SND_SOC_DAPM_EVENT_OFF(event);
gpio_set_value(QI_LB60_SND_GPIO, on); gpiod_set_value_cansleep(qi_lb60->snd_gpio, on);
gpio_set_value(QI_LB60_AMP_GPIO, on); gpiod_set_value_cansleep(qi_lb60->amp_gpio, on);
return 0; return 0;
} }
@ -46,29 +49,6 @@ static const struct snd_soc_dapm_route qi_lb60_routes[] = {
{"Speaker", NULL, "ROUT"}, {"Speaker", NULL, "ROUT"},
}; };
#define QI_LB60_DAIFMT (SND_SOC_DAIFMT_I2S | \
SND_SOC_DAIFMT_NB_NF | \
SND_SOC_DAIFMT_CBM_CFM)
static int qi_lb60_codec_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
struct snd_soc_dapm_context *dapm = &codec->dapm;
int ret;
snd_soc_dapm_nc_pin(dapm, "LIN");
snd_soc_dapm_nc_pin(dapm, "RIN");
ret = snd_soc_dai_set_fmt(cpu_dai, QI_LB60_DAIFMT);
if (ret < 0) {
dev_err(codec->dev, "Failed to set cpu dai format: %d\n", ret);
return ret;
}
return 0;
}
static struct snd_soc_dai_link qi_lb60_dai = { static struct snd_soc_dai_link qi_lb60_dai = {
.name = "jz4740", .name = "jz4740",
.stream_name = "jz4740", .stream_name = "jz4740",
@ -76,10 +56,11 @@ static struct snd_soc_dai_link qi_lb60_dai = {
.platform_name = "jz4740-i2s", .platform_name = "jz4740-i2s",
.codec_dai_name = "jz4740-hifi", .codec_dai_name = "jz4740-hifi",
.codec_name = "jz4740-codec", .codec_name = "jz4740-codec",
.init = qi_lb60_codec_init, .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBM_CFM,
}; };
static struct snd_soc_card qi_lb60 = { static struct snd_soc_card qi_lb60_card = {
.name = "QI LB60", .name = "QI LB60",
.owner = THIS_MODULE, .owner = THIS_MODULE,
.dai_link = &qi_lb60_dai, .dai_link = &qi_lb60_dai,
@ -89,40 +70,38 @@ static struct snd_soc_card qi_lb60 = {
.num_dapm_widgets = ARRAY_SIZE(qi_lb60_widgets), .num_dapm_widgets = ARRAY_SIZE(qi_lb60_widgets),
.dapm_routes = qi_lb60_routes, .dapm_routes = qi_lb60_routes,
.num_dapm_routes = ARRAY_SIZE(qi_lb60_routes), .num_dapm_routes = ARRAY_SIZE(qi_lb60_routes),
}; .fully_routed = true,
static const struct gpio qi_lb60_gpios[] = {
{ QI_LB60_SND_GPIO, GPIOF_OUT_INIT_LOW, "SND" },
{ QI_LB60_AMP_GPIO, GPIOF_OUT_INIT_LOW, "AMP" },
}; };
static int qi_lb60_probe(struct platform_device *pdev) static int qi_lb60_probe(struct platform_device *pdev)
{ {
struct snd_soc_card *card = &qi_lb60; struct qi_lb60 *qi_lb60;
struct snd_soc_card *card = &qi_lb60_card;
int ret; int ret;
ret = gpio_request_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios)); qi_lb60 = devm_kzalloc(&pdev->dev, sizeof(*qi_lb60), GFP_KERNEL);
if (!qi_lb60)
return -ENOMEM;
qi_lb60->snd_gpio = devm_gpiod_get(&pdev->dev, "snd");
if (IS_ERR(qi_lb60->snd_gpio))
return PTR_ERR(qi_lb60->snd_gpio);
ret = gpiod_direction_output(qi_lb60->snd_gpio, 0);
if (ret)
return ret;
qi_lb60->amp_gpio = devm_gpiod_get(&pdev->dev, "amp");
if (IS_ERR(qi_lb60->amp_gpio))
return PTR_ERR(qi_lb60->amp_gpio);
ret = gpiod_direction_output(qi_lb60->amp_gpio, 0);
if (ret) if (ret)
return ret; return ret;
card->dev = &pdev->dev; card->dev = &pdev->dev;
ret = snd_soc_register_card(card); snd_soc_card_set_drvdata(card, qi_lb60);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
ret);
gpio_free_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
}
return ret;
}
static int qi_lb60_remove(struct platform_device *pdev) return devm_snd_soc_register_card(&pdev->dev, card);
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
snd_soc_unregister_card(card);
gpio_free_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
return 0;
} }
static struct platform_driver qi_lb60_driver = { static struct platform_driver qi_lb60_driver = {
@ -131,7 +110,6 @@ static struct platform_driver qi_lb60_driver = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
}, },
.probe = qi_lb60_probe, .probe = qi_lb60_probe,
.remove = qi_lb60_remove,
}; };
module_platform_driver(qi_lb60_driver); module_platform_driver(qi_lb60_driver);

View file

@ -1266,6 +1266,50 @@ static void rtd_release(struct device *dev)
kfree(dev); kfree(dev);
} }
static int soc_aux_dev_init(struct snd_soc_card *card,
struct snd_soc_codec *codec,
int num)
{
struct snd_soc_aux_dev *aux_dev = &card->aux_dev[num];
struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
int ret;
rtd->card = card;
/* do machine specific initialization */
if (aux_dev->init) {
ret = aux_dev->init(&codec->dapm);
if (ret < 0)
return ret;
}
rtd->codec = codec;
return 0;
}
static int soc_dai_link_init(struct snd_soc_card *card,
struct snd_soc_codec *codec,
int num)
{
struct snd_soc_dai_link *dai_link = &card->dai_link[num];
struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
int ret;
rtd->card = card;
/* do machine specific initialization */
if (dai_link->init) {
ret = dai_link->init(rtd);
if (ret < 0)
return ret;
}
rtd->codec = codec;
return 0;
}
static int soc_post_component_init(struct snd_soc_card *card, static int soc_post_component_init(struct snd_soc_card *card,
struct snd_soc_codec *codec, struct snd_soc_codec *codec,
int num, int dailess) int num, int dailess)
@ -1280,26 +1324,20 @@ static int soc_post_component_init(struct snd_soc_card *card,
dai_link = &card->dai_link[num]; dai_link = &card->dai_link[num];
rtd = &card->rtd[num]; rtd = &card->rtd[num];
name = dai_link->name; name = dai_link->name;
ret = soc_dai_link_init(card, codec, num);
} else { } else {
aux_dev = &card->aux_dev[num]; aux_dev = &card->aux_dev[num];
rtd = &card->rtd_aux[num]; rtd = &card->rtd_aux[num];
name = aux_dev->name; name = aux_dev->name;
ret = soc_aux_dev_init(card, codec, num);
} }
rtd->card = card;
/* do machine specific initialization */
if (!dailess && dai_link->init)
ret = dai_link->init(rtd);
else if (dailess && aux_dev->init)
ret = aux_dev->init(&codec->dapm);
if (ret < 0) { if (ret < 0) {
dev_err(card->dev, "ASoC: failed to init %s: %d\n", name, ret); dev_err(card->dev, "ASoC: failed to init %s: %d\n", name, ret);
return ret; return ret;
} }
/* register the rtd device */ /* register the rtd device */
rtd->codec = codec;
rtd->dev = kzalloc(sizeof(struct device), GFP_KERNEL); rtd->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
if (!rtd->dev) if (!rtd->dev)
return -ENOMEM; return -ENOMEM;

View file

@ -1018,21 +1018,12 @@ static struct snd_soc_pcm_runtime *dpcm_get_be(struct snd_soc_card *card,
} }
static inline struct snd_soc_dapm_widget * static inline struct snd_soc_dapm_widget *
rtd_get_cpu_widget(struct snd_soc_pcm_runtime *rtd, int stream) dai_get_widget(struct snd_soc_dai *dai, int stream)
{ {
if (stream == SNDRV_PCM_STREAM_PLAYBACK) if (stream == SNDRV_PCM_STREAM_PLAYBACK)
return rtd->cpu_dai->playback_widget; return dai->playback_widget;
else else
return rtd->cpu_dai->capture_widget; return dai->capture_widget;
}
static inline struct snd_soc_dapm_widget *
rtd_get_codec_widget(struct snd_soc_pcm_runtime *rtd, int stream)
{
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
return rtd->codec_dai->playback_widget;
else
return rtd->codec_dai->capture_widget;
} }
static int widget_in_list(struct snd_soc_dapm_widget_list *list, static int widget_in_list(struct snd_soc_dapm_widget_list *list,
@ -1082,14 +1073,14 @@ static int dpcm_prune_paths(struct snd_soc_pcm_runtime *fe, int stream,
list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) { list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
/* is there a valid CPU DAI widget for this BE */ /* is there a valid CPU DAI widget for this BE */
widget = rtd_get_cpu_widget(dpcm->be, stream); widget = dai_get_widget(dpcm->be->cpu_dai, stream);
/* prune the BE if it's no longer in our active list */ /* prune the BE if it's no longer in our active list */
if (widget && widget_in_list(list, widget)) if (widget && widget_in_list(list, widget))
continue; continue;
/* is there a valid CODEC DAI widget for this BE */ /* is there a valid CODEC DAI widget for this BE */
widget = rtd_get_codec_widget(dpcm->be, stream); widget = dai_get_widget(dpcm->be->codec_dai, stream);
/* prune the BE if it's no longer in our active list */ /* prune the BE if it's no longer in our active list */
if (widget && widget_in_list(list, widget)) if (widget && widget_in_list(list, widget))