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:
commit
6630f30ed5
31 changed files with 745 additions and 431 deletions
|
@ -10,6 +10,9 @@ Optional properties:
|
|||
- fsl,mc13xxx-uses-touch : Indicate the touchscreen controller is being used
|
||||
|
||||
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
|
||||
"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
|
||||
|
|
16
Documentation/devicetree/bindings/sound/max98095.txt
Normal file
16
Documentation/devicetree/bindings/sound/max98095.txt
Normal 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>;
|
||||
};
|
|
@ -22,8 +22,6 @@ enum jz4740_dma_request_type {
|
|||
JZ4740_DMA_TYPE_UART_RECEIVE = 21,
|
||||
JZ4740_DMA_TYPE_SPI_TRANSMIT = 22,
|
||||
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_RECEIVE = 27,
|
||||
JZ4740_DMA_TYPE_TCU = 28,
|
||||
|
|
|
@ -425,6 +425,15 @@ static struct platform_device qi_lb60_audio_device = {
|
|||
.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 = {
|
||||
&jz4740_udc_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_mmc_device.dev.platform_data = &qi_lb60_mmc_pdata;
|
||||
|
||||
gpiod_add_lookup_table(&qi_lb60_audio_gpio_table);
|
||||
|
||||
jz4740_serial_device_register();
|
||||
|
||||
spi_register_board_info(qi_lb60_spi_board_info,
|
||||
|
|
|
@ -673,9 +673,13 @@ int mc13xxx_common_init(struct device *dev)
|
|||
if (mc13xxx->flags & MC13XXX_USE_ADC)
|
||||
mc13xxx_add_subdevice(mc13xxx, "%s-adc");
|
||||
|
||||
if (mc13xxx->flags & MC13XXX_USE_CODEC)
|
||||
mc13xxx_add_subdevice_pdata(mc13xxx, "%s-codec",
|
||||
pdata->codec, sizeof(*pdata->codec));
|
||||
if (mc13xxx->flags & MC13XXX_USE_CODEC) {
|
||||
if (pdata)
|
||||
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)
|
||||
mc13xxx_add_subdevice(mc13xxx, "%s-rtc");
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
*/
|
||||
#include <linux/module.h>
|
||||
#include <sound/soc.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
|
||||
#define DRV_NAME "hdmi-audio-codec"
|
||||
|
|
|
@ -11,10 +11,12 @@
|
|||
#include <linux/delay.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <sound/jack.h>
|
||||
#include <sound/pcm.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);
|
||||
snd_soc_update_bits(codec, M98090_REG_CLOCK_MODE,
|
||||
M98090_USE_M1_MASK, 0);
|
||||
max98090->master = false;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBM_CFM:
|
||||
/* Set to master mode */
|
||||
|
@ -1690,6 +1693,7 @@ static int max98090_dai_set_fmt(struct snd_soc_dai *codec_dai,
|
|||
regval |= M98090_MAS_MASK |
|
||||
M98090_BSEL_32;
|
||||
}
|
||||
max98090->master = true;
|
||||
break;
|
||||
case SND_SOC_DAIFMT_CBS_CFM:
|
||||
case SND_SOC_DAIFMT_CBM_CFS:
|
||||
|
@ -1793,13 +1797,6 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec,
|
|||
|
||||
switch (level) {
|
||||
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;
|
||||
|
||||
case SND_SOC_BIAS_PREPARE:
|
||||
|
@ -1873,7 +1870,8 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
|
|||
return -EINVAL;
|
||||
}
|
||||
|
||||
max98090_configure_bclk(codec);
|
||||
if (max98090->master)
|
||||
max98090_configure_bclk(codec);
|
||||
|
||||
cdata->rate = max98090->lrclk;
|
||||
|
||||
|
@ -1952,8 +1950,6 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
|
|||
|
||||
max98090->sysclk = freq;
|
||||
|
||||
max98090_configure_bclk(codec);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -2225,6 +2221,7 @@ static int max98090_probe(struct snd_soc_codec *codec)
|
|||
/* Initialize private data */
|
||||
|
||||
max98090->sysclk = (unsigned)-1;
|
||||
max98090->master = false;
|
||||
|
||||
cdata = &max98090->dai[0];
|
||||
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,
|
||||
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_add_widgets(codec);
|
||||
|
@ -2330,9 +2330,11 @@ static const struct regmap_config max98090_regmap = {
|
|||
};
|
||||
|
||||
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;
|
||||
const struct acpi_device_id *acpi_id;
|
||||
kernel_ulong_t driver_data = 0;
|
||||
int ret;
|
||||
|
||||
pr_debug("max98090_i2c_probe\n");
|
||||
|
@ -2342,7 +2344,19 @@ static int max98090_i2c_probe(struct i2c_client *i2c,
|
|||
if (max98090 == NULL)
|
||||
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);
|
||||
max98090->pdata = i2c->dev.platform_data;
|
||||
max98090->irq = i2c->irq;
|
||||
|
@ -2433,12 +2447,21 @@ static const struct of_device_id 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 = {
|
||||
.driver = {
|
||||
.name = "max98090",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &max98090_pm,
|
||||
.of_match_table = of_match_ptr(max98090_of_match),
|
||||
.acpi_match_table = ACPI_PTR(max98090_acpi_match),
|
||||
},
|
||||
.probe = max98090_i2c_probe,
|
||||
.remove = max98090_i2c_remove,
|
||||
|
|
|
@ -1540,6 +1540,7 @@ struct max98090_priv {
|
|||
unsigned int pa2en;
|
||||
unsigned int extmic_mux;
|
||||
unsigned int sidetone;
|
||||
bool master;
|
||||
};
|
||||
|
||||
int max98090_mic_detect(struct snd_soc_codec *codec,
|
||||
|
|
|
@ -2399,10 +2399,17 @@ static const struct i2c_device_id 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 = {
|
||||
.driver = {
|
||||
.name = "max98095",
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(max98095_of_match),
|
||||
},
|
||||
.probe = max98095_i2c_probe,
|
||||
.remove = max98095_i2c_remove,
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/mfd/mc13xxx.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
|
@ -748,6 +749,7 @@ static int __init mc13783_codec_probe(struct platform_device *pdev)
|
|||
{
|
||||
struct mc13783_priv *priv;
|
||||
struct mc13xxx_codec_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct device_node *np;
|
||||
int ret;
|
||||
|
||||
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->dac_ssi_port = pdata->dac_ssi_port;
|
||||
} 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);
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/gpio.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/spi/spi.h>
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <sound/soc.h>
|
||||
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <linux/i2c.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/slab.h>
|
||||
#include <sound/core.h>
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <sound/tpa6130a2-plat.h>
|
||||
#include <sound/soc.h>
|
||||
#include <sound/tlv.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_gpio.h>
|
||||
|
||||
#include "tpa6130a2.h"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.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
|
||||
|
||||
obj-$(CONFIG_SND_SST_MFLD_PLATFORM) += snd-soc-sst-mfld-platform.o
|
||||
|
|
|
@ -111,27 +111,13 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = {
|
|||
{
|
||||
.name = "Baytrail Audio",
|
||||
.stream_name = "Audio",
|
||||
.cpu_dai_name = "Front-cpu-dai",
|
||||
.cpu_dai_name = "baytrail-pcm-audio",
|
||||
.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 = 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,
|
||||
},
|
||||
};
|
||||
|
@ -146,6 +132,17 @@ static struct snd_soc_card byt_rt5640_card = {
|
|||
.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)
|
||||
{
|
||||
struct snd_soc_card *card = &byt_rt5640_card;
|
||||
|
@ -171,6 +168,7 @@ static struct platform_driver byt_rt5640_audio = {
|
|||
.driver = {
|
||||
.name = "byt-rt5640",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = BYT_RT5640_PM_OPS,
|
||||
},
|
||||
};
|
||||
module_platform_driver(byt_rt5640_audio)
|
||||
|
|
|
@ -214,6 +214,13 @@ static void sst_byt_boot(struct sst_dsp *sst)
|
|||
{
|
||||
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 */
|
||||
sst_dsp_shim_update_bits64(sst, SST_CSR, SST_BYT_CSR_STALL, 0x0);
|
||||
while (tries--) {
|
||||
|
@ -317,13 +324,6 @@ static int sst_byt_init(struct sst_dsp *sst, struct sst_pdata *pdata)
|
|||
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));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
|
@ -173,6 +173,7 @@ struct sst_byt {
|
|||
/* boot */
|
||||
wait_queue_head_t boot_wait;
|
||||
bool boot_complete;
|
||||
struct sst_fw *fw;
|
||||
|
||||
/* IPC messaging */
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
void *rx_data)
|
||||
{
|
||||
|
@ -661,36 +680,33 @@ int sst_byt_stream_free(struct sst_byt *byt, struct sst_byt_stream *stream)
|
|||
static int sst_byt_stream_operations(struct sst_byt *byt, int type,
|
||||
int stream_id, int wait)
|
||||
{
|
||||
struct sst_byt_start_stream_params start_stream;
|
||||
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)
|
||||
return sst_byt_ipc_tx_msg_wait(byt, header,
|
||||
tx_msg, size, NULL, 0);
|
||||
return sst_byt_ipc_tx_msg_wait(byt, header, NULL, 0, NULL, 0);
|
||||
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 */
|
||||
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;
|
||||
|
||||
ret = sst_byt_stream_operations(byt, IPC_IA_START_STREAM,
|
||||
stream->str_id, 0);
|
||||
start_stream.byte_offset = start_offset;
|
||||
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)
|
||||
dev_err(byt->dev, "ipc: error failed to start stream %d\n",
|
||||
stream->str_id);
|
||||
|
@ -782,6 +798,73 @@ static struct sst_dsp_device byt_dev = {
|
|||
.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)
|
||||
{
|
||||
struct sst_byt *byt;
|
||||
|
@ -848,6 +931,7 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
|
|||
}
|
||||
|
||||
pdata->dsp = byt;
|
||||
byt->fw = byt_sst_fw;
|
||||
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
/* 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_pause(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);
|
||||
void sst_byt_dsp_free(struct device *dev, struct sst_pdata *pdata);
|
||||
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
|
||||
|
|
|
@ -45,6 +45,11 @@ struct sst_byt_pcm_data {
|
|||
struct sst_byt_stream *stream;
|
||||
struct snd_pcm_substream *substream;
|
||||
struct mutex mutex;
|
||||
|
||||
/* latest DSP DMA hw pointer */
|
||||
u32 hw_ptr;
|
||||
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
/* 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 sst_byt_priv_data *pdata =
|
||||
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;
|
||||
u32 rate, bits;
|
||||
u8 channels;
|
||||
|
@ -130,21 +135,56 @@ static int sst_byt_pcm_hw_free(struct snd_pcm_substream *substream)
|
|||
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)
|
||||
{
|
||||
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 = snd_soc_pcm_get_drvdata(rtd);
|
||||
struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
|
||||
struct sst_byt *byt = pdata->byt;
|
||||
|
||||
dev_dbg(rtd->dev, "PCM: trigger %d\n", cmd);
|
||||
|
||||
switch (cmd) {
|
||||
case SNDRV_PCM_TRIGGER_START:
|
||||
sst_byt_stream_start(byt, pcm_data->stream);
|
||||
sst_byt_stream_start(byt, pcm_data->stream, 0);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_RESUME:
|
||||
schedule_work(&pcm_data->work);
|
||||
break;
|
||||
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
|
||||
sst_byt_stream_resume(byt, pcm_data->stream);
|
||||
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_runtime *runtime = substream->runtime;
|
||||
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,
|
||||
(runtime->control->appl_ptr %
|
||||
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);
|
||||
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 sst_byt_priv_data *pdata =
|
||||
snd_soc_platform_get_drvdata(rtd->platform);
|
||||
struct sst_byt_pcm_data *pcm_data = snd_soc_pcm_get_drvdata(rtd);
|
||||
struct sst_byt *byt = pdata->byt;
|
||||
snd_pcm_uframes_t offset;
|
||||
int pos;
|
||||
struct sst_byt_pcm_data *pcm_data = &pdata->pcm[substream->stream];
|
||||
|
||||
pos = sst_byt_get_dsp_position(byt, pcm_data->stream,
|
||||
snd_pcm_lib_buffer_bytes(substream));
|
||||
offset = bytes_to_frames(runtime, pos);
|
||||
dev_dbg(rtd->dev, "PCM: DMA pointer %u bytes\n", pcm_data->hw_ptr);
|
||||
|
||||
dev_dbg(rtd->dev, "PCM: DMA pointer %zu bytes\n",
|
||||
frames_to_bytes(runtime, (u32)offset));
|
||||
return offset;
|
||||
return bytes_to_frames(runtime, pcm_data->hw_ptr);
|
||||
}
|
||||
|
||||
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 sst_byt_priv_data *pdata =
|
||||
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;
|
||||
|
||||
dev_dbg(rtd->dev, "PCM: open\n");
|
||||
|
||||
pcm_data = &pdata->pcm[rtd->cpu_dai->id];
|
||||
mutex_lock(&pcm_data->mutex);
|
||||
|
||||
snd_soc_pcm_set_drvdata(rtd, pcm_data);
|
||||
pcm_data->substream = substream;
|
||||
|
||||
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);
|
||||
if (pcm_data->stream == NULL) {
|
||||
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 sst_byt_priv_data *pdata =
|
||||
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;
|
||||
int ret;
|
||||
|
||||
dev_dbg(rtd->dev, "PCM: close\n");
|
||||
|
||||
cancel_work_sync(&pcm_data->work);
|
||||
mutex_lock(&pcm_data->mutex);
|
||||
ret = sst_byt_stream_free(byt, pcm_data->stream);
|
||||
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;
|
||||
size_t size;
|
||||
struct snd_soc_platform *platform = rtd->platform;
|
||||
struct sst_pdata *pdata = dev_get_platdata(platform->dev);
|
||||
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 ||
|
||||
pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
|
||||
size = sst_byt_pcm_hardware.buffer_bytes_max;
|
||||
ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
|
||||
SNDRV_DMA_TYPE_DEV,
|
||||
rtd->card->dev,
|
||||
pdata->dma_dev,
|
||||
size, size);
|
||||
if (ret) {
|
||||
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[] = {
|
||||
{
|
||||
.name = "Front-cpu-dai",
|
||||
.name = "Baytrail PCM",
|
||||
.playback = {
|
||||
.stream_name = "System Playback",
|
||||
.channels_min = 2,
|
||||
|
@ -317,9 +353,6 @@ static struct snd_soc_dai_driver byt_dais[] = {
|
|||
.formats = SNDRV_PCM_FMTBIT_S24_3LE |
|
||||
SNDRV_PCM_FMTBIT_S16_LE,
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "Mic1-cpu-dai",
|
||||
.capture = {
|
||||
.stream_name = "Analog Capture",
|
||||
.channels_min = 2,
|
||||
|
@ -344,8 +377,10 @@ static int sst_byt_pcm_probe(struct snd_soc_platform *platform)
|
|||
priv_data->byt = plat_data->dsp;
|
||||
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);
|
||||
INIT_WORK(&priv_data->pcm[i].work, sst_byt_pcm_work);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -367,6 +402,72 @@ static const struct snd_soc_component_driver byt_dai_component = {
|
|||
.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)
|
||||
{
|
||||
struct sst_pdata *sst_pdata = dev_get_platdata(&pdev->dev);
|
||||
|
@ -409,6 +510,7 @@ static struct platform_driver sst_byt_pcm_driver = {
|
|||
.driver = {
|
||||
.name = "baytrail-pcm-audio",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = SST_BYT_PM_OPS,
|
||||
},
|
||||
|
||||
.probe = sst_byt_pcm_dev_probe,
|
||||
|
|
|
@ -284,6 +284,8 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
|
|||
const struct firmware *fw, void *private);
|
||||
void sst_fw_free(struct sst_fw *sst_fw);
|
||||
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 */
|
||||
struct sst_module *sst_module_new(struct sst_fw *sst_fw,
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include "sst-dsp.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)
|
||||
{
|
||||
u32 i;
|
||||
|
@ -91,6 +93,42 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
|
|||
}
|
||||
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 */
|
||||
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 */
|
||||
list_for_each_entry(sst_module, &dsp->module_list, list) {
|
||||
if (scratch->s.size > sst_module->s.size)
|
||||
scratch->s.size = scratch->s.size;
|
||||
else
|
||||
if (scratch->s.size < sst_module->s.size)
|
||||
scratch->s.size = sst_module->s.size;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,10 +16,6 @@
|
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* 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 {
|
||||
u16 codec; /* codec type */
|
||||
u8 num_chan; /* 1=Mono, 2=Stereo */
|
||||
u8 pcm_wd_sz; /* 16/24 - bit*/
|
||||
u32 reserved; /* Bitrate in bits per second */
|
||||
|
@ -53,7 +48,6 @@ struct snd_pcm_params {
|
|||
|
||||
/* MP3 Music Parameters Message */
|
||||
struct snd_mp3_params {
|
||||
u16 codec;
|
||||
u8 num_chan; /* 1=Mono, 2=Stereo */
|
||||
u8 pcm_wd_sz; /* 16/24 - bit*/
|
||||
u8 crc_check; /* crc_check - disable (0) or enable (1) */
|
||||
|
@ -67,7 +61,6 @@ struct snd_mp3_params {
|
|||
|
||||
/* AAC Music Parameters Message */
|
||||
struct snd_aac_params {
|
||||
u16 codec;
|
||||
u8 num_chan; /* 1=Mono, 2=Stereo*/
|
||||
u8 pcm_wd_sz; /* 16/24 - bit*/
|
||||
u8 bdownsample; /*SBR downsampling 0 - disable 1 -enabled AAC+ only */
|
||||
|
@ -81,7 +74,6 @@ struct snd_aac_params {
|
|||
|
||||
/* WMA Music Parameters Message */
|
||||
struct snd_wma_params {
|
||||
u16 codec;
|
||||
u8 num_chan; /* 1=Mono, 2=Stereo */
|
||||
u8 pcm_wd_sz; /* 16/24 - bit*/
|
||||
u32 brate; /* Use the hard coded value. */
|
||||
|
|
237
sound/soc/intel/sst-mfld-platform-compress.c
Normal file
237
sound/soc/intel/sst-mfld-platform-compress.c
Normal 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,
|
||||
};
|
|
@ -15,13 +15,7 @@
|
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* 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
|
||||
|
||||
|
@ -35,8 +29,9 @@
|
|||
#include <sound/compress_driver.h>
|
||||
#include "sst-mfld-platform.h"
|
||||
|
||||
static struct sst_device *sst;
|
||||
struct sst_device *sst;
|
||||
static DEFINE_MUTEX(sst_lock);
|
||||
extern struct snd_compr_ops sst_platform_compr_ops;
|
||||
|
||||
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,
|
||||
},
|
||||
},
|
||||
{
|
||||
.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",
|
||||
.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 */
|
||||
static inline void sst_set_stream_status(struct sst_runtime_stream *stream,
|
||||
void sst_set_stream_status(struct sst_runtime_stream *stream,
|
||||
int state)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
@ -186,7 +147,6 @@ static void sst_fill_pcm_params(struct snd_pcm_substream *substream,
|
|||
struct sst_pcm_params *param)
|
||||
{
|
||||
|
||||
param->codec = SST_CODEC_TYPE_PCM;
|
||||
param->num_chan = (u8) substream->runtime->channels;
|
||||
param->pcm_wd_sz = substream->runtime->sample_bits;
|
||||
param->reserved = 0;
|
||||
|
@ -471,205 +431,6 @@ static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd)
|
|||
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 = {
|
||||
.ops = &sst_platform_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,
|
||||
};
|
||||
|
||||
static const struct snd_soc_component_driver sst_component = {
|
||||
.name = "sst",
|
||||
};
|
||||
|
||||
|
||||
static int sst_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret;
|
|
@ -15,13 +15,7 @@
|
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* 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__
|
||||
|
@ -29,6 +23,8 @@
|
|||
|
||||
#include "sst-mfld-dsp.h"
|
||||
|
||||
extern struct sst_device *sst;
|
||||
|
||||
#define SST_MONO 1
|
||||
#define SST_STEREO 2
|
||||
#define SST_MAX_CAP 5
|
||||
|
@ -108,6 +104,8 @@ struct sst_stream_params {
|
|||
struct sst_compress_cb {
|
||||
void *param;
|
||||
void (*compr_cb)(void *param);
|
||||
void *drain_cb_param;
|
||||
void (*drain_notify)(void *param);
|
||||
};
|
||||
|
||||
struct compress_sst_ops {
|
||||
|
@ -148,6 +146,7 @@ struct sst_device {
|
|||
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_unregister_dsp(struct sst_device *sst);
|
||||
#endif
|
||||
|
|
|
@ -1,24 +1,29 @@
|
|||
config SND_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
|
||||
help
|
||||
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
|
||||
interfaces to support below.
|
||||
|
||||
if SND_JZ4740_SOC
|
||||
|
||||
config SND_JZ4740_SOC_I2S
|
||||
depends on SND_JZ4740_SOC
|
||||
tristate "SoC Audio (I2S protocol) for Ingenic JZ4740 SoC"
|
||||
depends on HAS_IOMEM
|
||||
help
|
||||
Say Y if you want to use I2S protocol and I2S codec on Ingenic JZ4740
|
||||
based boards.
|
||||
|
||||
config SND_JZ4740_SOC_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_SOC_JZ4740_CODEC
|
||||
help
|
||||
Say Y if you want to add support for ASoC audio on the Qi LB60 board
|
||||
a.k.a Qi Ben NanoNote.
|
||||
|
||||
endif
|
||||
|
|
|
@ -31,10 +31,11 @@
|
|||
#include <sound/initval.h>
|
||||
#include <sound/dmaengine_pcm.h>
|
||||
|
||||
#include <asm/mach-jz4740/dma.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_CTRL 0x04
|
||||
#define JZ_REG_AIC_I2S_FMT 0x10
|
||||
|
|
|
@ -19,18 +19,21 @@
|
|||
#include <sound/core.h>
|
||||
#include <sound/pcm.h>
|
||||
#include <sound/soc.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#define QI_LB60_SND_GPIO JZ_GPIO_PORTB(29)
|
||||
#define QI_LB60_AMP_GPIO JZ_GPIO_PORTD(4)
|
||||
struct qi_lb60 {
|
||||
struct gpio_desc *snd_gpio;
|
||||
struct gpio_desc *amp_gpio;
|
||||
};
|
||||
|
||||
static int qi_lb60_spk_event(struct snd_soc_dapm_widget *widget,
|
||||
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);
|
||||
|
||||
gpio_set_value(QI_LB60_SND_GPIO, on);
|
||||
gpio_set_value(QI_LB60_AMP_GPIO, on);
|
||||
gpiod_set_value_cansleep(qi_lb60->snd_gpio, on);
|
||||
gpiod_set_value_cansleep(qi_lb60->amp_gpio, on);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -46,29 +49,6 @@ static const struct snd_soc_dapm_route qi_lb60_routes[] = {
|
|||
{"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 = {
|
||||
.name = "jz4740",
|
||||
.stream_name = "jz4740",
|
||||
|
@ -76,10 +56,11 @@ static struct snd_soc_dai_link qi_lb60_dai = {
|
|||
.platform_name = "jz4740-i2s",
|
||||
.codec_dai_name = "jz4740-hifi",
|
||||
.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",
|
||||
.owner = THIS_MODULE,
|
||||
.dai_link = &qi_lb60_dai,
|
||||
|
@ -89,40 +70,38 @@ static struct snd_soc_card qi_lb60 = {
|
|||
.num_dapm_widgets = ARRAY_SIZE(qi_lb60_widgets),
|
||||
.dapm_routes = qi_lb60_routes,
|
||||
.num_dapm_routes = ARRAY_SIZE(qi_lb60_routes),
|
||||
};
|
||||
|
||||
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" },
|
||||
.fully_routed = true,
|
||||
};
|
||||
|
||||
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;
|
||||
|
||||
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)
|
||||
return ret;
|
||||
|
||||
card->dev = &pdev->dev;
|
||||
|
||||
ret = snd_soc_register_card(card);
|
||||
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;
|
||||
}
|
||||
snd_soc_card_set_drvdata(card, qi_lb60);
|
||||
|
||||
static int qi_lb60_remove(struct platform_device *pdev)
|
||||
{
|
||||
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;
|
||||
return devm_snd_soc_register_card(&pdev->dev, card);
|
||||
}
|
||||
|
||||
static struct platform_driver qi_lb60_driver = {
|
||||
|
@ -131,7 +110,6 @@ static struct platform_driver qi_lb60_driver = {
|
|||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = qi_lb60_probe,
|
||||
.remove = qi_lb60_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(qi_lb60_driver);
|
||||
|
|
|
@ -1266,6 +1266,50 @@ static void rtd_release(struct device *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,
|
||||
struct snd_soc_codec *codec,
|
||||
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];
|
||||
rtd = &card->rtd[num];
|
||||
name = dai_link->name;
|
||||
ret = soc_dai_link_init(card, codec, num);
|
||||
} else {
|
||||
aux_dev = &card->aux_dev[num];
|
||||
rtd = &card->rtd_aux[num];
|
||||
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) {
|
||||
dev_err(card->dev, "ASoC: failed to init %s: %d\n", name, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* register the rtd device */
|
||||
rtd->codec = codec;
|
||||
|
||||
rtd->dev = kzalloc(sizeof(struct device), GFP_KERNEL);
|
||||
if (!rtd->dev)
|
||||
return -ENOMEM;
|
||||
|
|
|
@ -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 *
|
||||
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)
|
||||
return rtd->cpu_dai->playback_widget;
|
||||
return dai->playback_widget;
|
||||
else
|
||||
return rtd->cpu_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;
|
||||
return dai->capture_widget;
|
||||
}
|
||||
|
||||
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) {
|
||||
|
||||
/* 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 */
|
||||
if (widget && widget_in_list(list, widget))
|
||||
continue;
|
||||
|
||||
/* 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 */
|
||||
if (widget && widget_in_list(list, widget))
|
||||
|
|
Loading…
Reference in a new issue