Commit a6ce3052 authored by Mark Brown's avatar Mark Brown

Merge remote-tracking branches 'asoc/topic/intel', 'asoc/topic/kirkwood',...

Merge remote-tracking branches 'asoc/topic/intel', 'asoc/topic/kirkwood', 'asoc/topic/max98090' and 'asoc/topic/mc13783' into asoc-next
......@@ -4,7 +4,7 @@ This device supports I2C only.
Required properties:
- compatible : "maxim,max98090".
- compatible : "maxim,max98090" or "maxim,max98091".
- reg : The I2C address of the device.
......
/*
* platform_sst_audio.h: sst audio platform data header file
*
* Copyright (C) 2012-14 Intel Corporation
* Author: Jeeja KP <jeeja.kp@intel.com>
* Omair Mohammed Abdullah <omair.m.abdullah@intel.com>
* 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.
*/
#ifndef _PLATFORM_SST_AUDIO_H_
#define _PLATFORM_SST_AUDIO_H_
#include <linux/sfi.h>
enum sst_audio_task_id_mrfld {
SST_TASK_ID_NONE = 0,
SST_TASK_ID_SBA = 1,
SST_TASK_ID_MEDIA = 3,
SST_TASK_ID_MAX = SST_TASK_ID_MEDIA,
};
/* Device IDs for Merrifield are Pipe IDs,
* ref: DSP spec v0.75 */
enum sst_audio_device_id_mrfld {
/* Output pipeline IDs */
PIPE_ID_OUT_START = 0x0,
PIPE_CODEC_OUT0 = 0x2,
PIPE_CODEC_OUT1 = 0x3,
PIPE_SPROT_LOOP_OUT = 0x4,
PIPE_MEDIA_LOOP1_OUT = 0x5,
PIPE_MEDIA_LOOP2_OUT = 0x6,
PIPE_VOIP_OUT = 0xC,
PIPE_PCM0_OUT = 0xD,
PIPE_PCM1_OUT = 0xE,
PIPE_PCM2_OUT = 0xF,
PIPE_MEDIA0_OUT = 0x12,
PIPE_MEDIA1_OUT = 0x13,
/* Input Pipeline IDs */
PIPE_ID_IN_START = 0x80,
PIPE_CODEC_IN0 = 0x82,
PIPE_CODEC_IN1 = 0x83,
PIPE_SPROT_LOOP_IN = 0x84,
PIPE_MEDIA_LOOP1_IN = 0x85,
PIPE_MEDIA_LOOP2_IN = 0x86,
PIPE_VOIP_IN = 0x8C,
PIPE_PCM0_IN = 0x8D,
PIPE_PCM1_IN = 0x8E,
PIPE_MEDIA0_IN = 0x8F,
PIPE_MEDIA1_IN = 0x90,
PIPE_MEDIA2_IN = 0x91,
PIPE_RSVD = 0xFF,
};
/* The stream map for each platform consists of an array of the below
* stream map structure.
*/
struct sst_dev_stream_map {
u8 dev_num; /* device id */
u8 subdev_num; /* substream */
u8 direction;
u8 device_id; /* fw id */
u8 task_id; /* fw task */
u8 status;
};
struct sst_platform_data {
/* Intel software platform id*/
struct sst_dev_stream_map *pdev_strm_map;
unsigned int strm_map_size;
};
int add_sst_platform_device(void);
#endif
/*
* linux/sound/rt286.h -- Platform data for RT286
*
* Copyright 2013 Realtek Microelectronics
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __LINUX_SND_RT286_H
#define __LINUX_SND_RT286_H
struct rt286_platform_data {
bool cbj_en; /*combo jack enable*/
bool gpio2_en; /*GPIO2 enable*/
};
#endif
......@@ -75,6 +75,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_PCM3008
select SND_SOC_PCM512x_I2C if I2C
select SND_SOC_PCM512x_SPI if SPI_MASTER
select SND_SOC_RT286 if I2C
select SND_SOC_RT5631 if I2C
select SND_SOC_RT5640 if I2C
select SND_SOC_RT5645 if I2C
......@@ -455,6 +456,9 @@ config SND_SOC_RL6231
default m if SND_SOC_RT5645=m
default m if SND_SOC_RT5651=m
config SND_SOC_RT286
tristate
config SND_SOC_RT5631
tristate
......
......@@ -69,6 +69,7 @@ snd-soc-pcm512x-objs := pcm512x.o
snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
snd-soc-pcm512x-spi-objs := pcm512x-spi.o
snd-soc-rl6231-objs := rl6231.o
snd-soc-rt286-objs := rt286.o
snd-soc-rt5631-objs := rt5631.o
snd-soc-rt5640-objs := rt5640.o
snd-soc-rt5645-objs := rt5645.o
......@@ -237,6 +238,7 @@ obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o
obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o
obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o
obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o
obj-$(CONFIG_SND_SOC_RT286) += snd-soc-rt286.o
obj-$(CONFIG_SND_SOC_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o
......
......@@ -26,10 +26,6 @@
#include <sound/max98090.h>
#include "max98090.h"
#define DEBUG
#define EXTMIC_METHOD
#define EXTMIC_METHOD_TEST
/* Allows for sparsely populated register maps */
static struct reg_default max98090_reg[] = {
{ 0x00, 0x00 }, /* 00 Software Reset */
......@@ -820,7 +816,6 @@ static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
else
val = (val & M98090_MIC_PA2EN_MASK) >> M98090_MIC_PA2EN_SHIFT;
if (val >= 1) {
if (w->reg == M98090_REG_MIC1_INPUT_LEVEL) {
max98090->pa1en = val - 1; /* Update for volatile */
......@@ -1140,7 +1135,6 @@ static const struct snd_kcontrol_new max98090_mixhprsel_mux =
SOC_DAPM_ENUM("MIXHPRSEL Mux", mixhprsel_mux_enum);
static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("MIC1"),
SND_SOC_DAPM_INPUT("MIC2"),
SND_SOC_DAPM_INPUT("DMICL"),
......@@ -1304,7 +1298,6 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
};
static const struct snd_soc_dapm_widget max98091_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("DMIC3"),
SND_SOC_DAPM_INPUT("DMIC4"),
......@@ -1315,7 +1308,6 @@ static const struct snd_soc_dapm_widget max98091_dapm_widgets[] = {
};
static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
{"MIC1 Input", NULL, "MIC1"},
{"MIC2 Input", NULL, "MIC2"},
......@@ -1493,17 +1485,14 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
{"SPKR", NULL, "SPK Right Out"},
{"RCVL", NULL, "RCV Left Out"},
{"RCVR", NULL, "RCV Right Out"},
};
static const struct snd_soc_dapm_route max98091_dapm_routes[] = {
/* DMIC inputs */
{"DMIC3", NULL, "DMIC3_ENA"},
{"DMIC4", NULL, "DMIC4_ENA"},
{"DMIC3", NULL, "AHPF"},
{"DMIC4", NULL, "AHPF"},
};
static int max98090_add_widgets(struct snd_soc_codec *codec)
......@@ -1531,7 +1520,6 @@ static int max98090_add_widgets(struct snd_soc_codec *codec)
snd_soc_dapm_add_routes(dapm, max98091_dapm_routes,
ARRAY_SIZE(max98091_dapm_routes));
}
return 0;
......@@ -2212,22 +2200,11 @@ static struct snd_soc_dai_driver max98090_dai[] = {
}
};
static void max98090_handle_pdata(struct snd_soc_codec *codec)
{
struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
struct max98090_pdata *pdata = max98090->pdata;
if (!pdata) {
dev_err(codec->dev, "No platform data\n");
return;
}
}
static int max98090_probe(struct snd_soc_codec *codec)
{
struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
struct max98090_cdata *cdata;
enum max98090_type devtype;
int ret = 0;
dev_dbg(codec->dev, "max98090_probe\n");
......@@ -2263,16 +2240,21 @@ static int max98090_probe(struct snd_soc_codec *codec)
}
if ((ret >= M98090_REVA) && (ret <= M98090_REVA + 0x0f)) {
max98090->devtype = MAX98090;
devtype = MAX98090;
dev_info(codec->dev, "MAX98090 REVID=0x%02x\n", ret);
} else if ((ret >= M98091_REVA) && (ret <= M98091_REVA + 0x0f)) {
max98090->devtype = MAX98091;
devtype = MAX98091;
dev_info(codec->dev, "MAX98091 REVID=0x%02x\n", ret);
} else {
max98090->devtype = MAX98090;
devtype = MAX98090;
dev_err(codec->dev, "Unrecognized revision 0x%02x\n", ret);
}
if (max98090->devtype != devtype) {
dev_warn(codec->dev, "Mismatch in DT specified CODEC type.\n");
max98090->devtype = devtype;
}
max98090->jack_state = M98090_JACK_STATE_NO_HEADSET;
INIT_DELAYED_WORK(&max98090->jack_work, max98090_jack_work);
......@@ -2317,8 +2299,6 @@ static int max98090_probe(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE,
M98090_MBVSEL_MASK, M98090_MBVSEL_2V8);
max98090_handle_pdata(codec);
max98090_add_widgets(codec);
err_access:
......@@ -2428,7 +2408,7 @@ static int max98090_runtime_suspend(struct device *dev)
}
#endif
#ifdef CONFIG_PM
#ifdef CONFIG_PM_SLEEP
static int max98090_resume(struct device *dev)
{
struct max98090_priv *max98090 = dev_get_drvdata(dev);
......@@ -2460,12 +2440,14 @@ static const struct dev_pm_ops max98090_pm = {
static const struct i2c_device_id max98090_i2c_id[] = {
{ "max98090", MAX98090 },
{ "max98091", MAX98091 },
{ }
};
MODULE_DEVICE_TABLE(i2c, max98090_i2c_id);
static const struct of_device_id max98090_of_match[] = {
{ .compatible = "maxim,max98090", },
{ .compatible = "maxim,max98091", },
{ }
};
MODULE_DEVICE_TABLE(of, max98090_of_match);
......
......@@ -766,11 +766,11 @@ static int __init mc13783_codec_probe(struct platform_device *pdev)
ret = of_property_read_u32(np, "adc-port", &priv->adc_ssi_port);
if (ret)
return ret;
goto out;
ret = of_property_read_u32(np, "dac-port", &priv->dac_ssi_port);
if (ret)
return ret;
goto out;
}
dev_set_drvdata(&pdev->dev, priv);
......@@ -783,6 +783,8 @@ static int __init mc13783_codec_probe(struct platform_device *pdev)
ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783,
mc13783_dai_async, ARRAY_SIZE(mc13783_dai_async));
out:
of_node_put(np);
return ret;
}
......
/*
* rt286.c -- RT286 ALSA SoC audio codec driver
*
* Copyright 2013 Realtek Semiconductor Corp.
* Author: Bard Liao <bardliao@realtek.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/platform_device.h>
#include <linux/spi/spi.h>
#include <linux/acpi.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/soc-dapm.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <sound/jack.h>
#include <linux/workqueue.h>
#include <sound/rt286.h>
#include <sound/hda_verbs.h>
#include "rt286.h"
#define RT286_VENDOR_ID 0x10ec0286
struct rt286_priv {
struct regmap *regmap;
struct snd_soc_codec *codec;
struct rt286_platform_data pdata;
struct i2c_client *i2c;
struct snd_soc_jack *jack;
struct delayed_work jack_detect_work;
int sys_clk;
struct reg_default *index_cache;
};
static struct reg_default rt286_index_def[] = {
{ 0x01, 0xaaaa },
{ 0x02, 0x8aaa },
{ 0x03, 0x0002 },
{ 0x04, 0xaf01 },
{ 0x08, 0x000d },
{ 0x09, 0xd810 },
{ 0x0a, 0x0060 },
{ 0x0b, 0x0000 },
{ 0x0d, 0x2800 },
{ 0x0f, 0x0000 },
{ 0x19, 0x0a17 },
{ 0x20, 0x0020 },
{ 0x33, 0x0208 },
{ 0x49, 0x0004 },
{ 0x4f, 0x50e9 },
{ 0x50, 0x2c00 },
{ 0x63, 0x2902 },
{ 0x67, 0x1111 },
{ 0x68, 0x1016 },
{ 0x69, 0x273f },
};
#define INDEX_CACHE_SIZE ARRAY_SIZE(rt286_index_def)
static const struct reg_default rt286_reg[] = {
{ 0x00170500, 0x00000400 },
{ 0x00220000, 0x00000031 },
{ 0x00239000, 0x0000007f },
{ 0x0023a000, 0x0000007f },
{ 0x00270500, 0x00000400 },
{ 0x00370500, 0x00000400 },
{ 0x00870500, 0x00000400 },
{ 0x00920000, 0x00000031 },
{ 0x00935000, 0x000000c3 },
{ 0x00936000, 0x000000c3 },
{ 0x00970500, 0x00000400 },
{ 0x00b37000, 0x00000097 },
{ 0x00b37200, 0x00000097 },
{ 0x00b37300, 0x00000097 },
{ 0x00c37000, 0x00000000 },
{ 0x00c37100, 0x00000080 },
{ 0x01270500, 0x00000400 },
{ 0x01370500, 0x00000400 },
{ 0x01371f00, 0x411111f0 },
{ 0x01439000, 0x00000080 },
{ 0x0143a000, 0x00000080 },
{ 0x01470700, 0x00000000 },
{ 0x01470500, 0x00000400 },
{ 0x01470c00, 0x00000000 },
{ 0x01470100, 0x00000000 },
{ 0x01837000, 0x00000000 },
{ 0x01870500, 0x00000400 },
{ 0x02050000, 0x00000000 },
{ 0x02139000, 0x00000080 },
{ 0x0213a000, 0x00000080 },
{ 0x02170100, 0x00000000 },
{ 0x02170500, 0x00000400 },
{ 0x02170700, 0x00000000 },
{ 0x02270100, 0x00000000 },
{ 0x02370100, 0x00000000 },
{ 0x02040000, 0x00004002 },
{ 0x01870700, 0x00000020 },
{ 0x00830000, 0x000000c3 },
{ 0x00930000, 0x000000c3 },
{ 0x01270700, 0x00000000 },
};
static bool rt286_volatile_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case 0 ... 0xff:
case RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID):
case RT286_GET_HP_SENSE:
case RT286_GET_MIC1_SENSE:
case RT286_PROC_COEF:
return true;
default:
return false;
}
}
static bool rt286_readable_register(struct device *dev, unsigned int reg)
{
switch (reg) {
case 0 ... 0xff:
case RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID):
case RT286_GET_HP_SENSE:
case RT286_GET_MIC1_SENSE:
case RT286_SET_AUDIO_POWER:
case RT286_SET_HPO_POWER:
case RT286_SET_SPK_POWER:
case RT286_SET_DMIC1_POWER:
case RT286_SPK_MUX:
case RT286_HPO_MUX:
case RT286_ADC0_MUX:
case RT286_ADC1_MUX:
case RT286_SET_MIC1:
case RT286_SET_PIN_HPO:
case RT286_SET_PIN_SPK:
case RT286_SET_PIN_DMIC1:
case RT286_SPK_EAPD:
case RT286_SET_AMP_GAIN_HPO:
case RT286_SET_DMIC2_DEFAULT:
case RT286_DACL_GAIN:
case RT286_DACR_GAIN:
case RT286_ADCL_GAIN:
case RT286_ADCR_GAIN:
case RT286_MIC_GAIN:
case RT286_SPOL_GAIN:
case RT286_SPOR_GAIN:
case RT286_HPOL_GAIN:
case RT286_HPOR_GAIN:
case RT286_F_DAC_SWITCH:
case RT286_F_RECMIX_SWITCH:
case RT286_REC_MIC_SWITCH:
case RT286_REC_I2S_SWITCH:
case RT286_REC_LINE_SWITCH:
case RT286_REC_BEEP_SWITCH:
case RT286_DAC_FORMAT:
case RT286_ADC_FORMAT:
case RT286_COEF_INDEX:
case RT286_PROC_COEF:
case RT286_SET_AMP_GAIN_ADC_IN1:
case RT286_SET_AMP_GAIN_ADC_IN2:
case RT286_SET_POWER(RT286_DAC_OUT1):
case RT286_SET_POWER(RT286_DAC_OUT2):
case RT286_SET_POWER(RT286_ADC_IN1):
case RT286_SET_POWER(RT286_ADC_IN2):
case RT286_SET_POWER(RT286_DMIC2):
case RT286_SET_POWER(RT286_MIC1):
return true;
default:
return false;
}
}
static int rt286_hw_write(void *context, unsigned int reg, unsigned int value)
{
struct i2c_client *client = context;
struct rt286_priv *rt286 = i2c_get_clientdata(client);
u8 data[4];
int ret, i;
/*handle index registers*/
if (reg <= 0xff) {
rt286_hw_write(client, RT286_COEF_INDEX, reg);
reg = RT286_PROC_COEF;
for (i = 0; i < INDEX_CACHE_SIZE; i++) {
if (reg == rt286->index_cache[i].reg) {
rt286->index_cache[i].def = value;
break;
}
}
}
data[0] = (reg >> 24) & 0xff;
data[1] = (reg >> 16) & 0xff;
/*
* 4 bit VID: reg should be 0
* 12 bit VID: value should be 0
* So we use an OR operator to handle it rather than use if condition.
*/
data[2] = ((reg >> 8) & 0xff) | ((value >> 8) & 0xff);
data[3] = value & 0xff;
ret = i2c_master_send(client, data, 4);
if (ret == 4)
return 0;
else
pr_err("ret=%d\n", ret);
if (ret < 0)
return ret;
else
return -EIO;
}
static int rt286_hw_read(void *context, unsigned int reg, unsigned int *value)
{
struct i2c_client *client = context;
struct i2c_msg xfer[2];
int ret;
__be32 be_reg;
unsigned int index, vid, buf = 0x0;
/*handle index registers*/
if (reg <= 0xff) {
rt286_hw_write(client, RT286_COEF_INDEX, reg);
reg = RT286_PROC_COEF;
}
reg = reg | 0x80000;
vid = (reg >> 8) & 0xfff;
if (AC_VERB_GET_AMP_GAIN_MUTE == (vid & 0xf00)) {
index = (reg >> 8) & 0xf;
reg = (reg & ~0xf0f) | index;
}
be_reg = cpu_to_be32(reg);
/* Write register */
xfer[0].addr = client->addr;
xfer[0].flags = 0;
xfer[0].len = 4;
xfer[0].buf = (u8 *)&be_reg;
/* Read data */
xfer[1].addr = client->addr;
xfer[1].flags = I2C_M_RD;
xfer[1].len = 4;
xfer[1].buf = (u8 *)&buf;
ret = i2c_transfer(client->adapter, xfer, 2);
if (ret < 0)
return ret;
else if (ret != 2)
return -EIO;
*value = be32_to_cpu(buf);
return 0;
}
static void rt286_index_sync(struct snd_soc_codec *codec)
{
struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
int i;
for (i = 0; i < INDEX_CACHE_SIZE; i++) {
snd_soc_write(codec, rt286->index_cache[i].reg,
rt286->index_cache[i].def);
}
}
static int rt286_support_power_controls[] = {
RT286_DAC_OUT1,
RT286_DAC_OUT2,
RT286_ADC_IN1,
RT286_ADC_IN2,
RT286_MIC1,
RT286_DMIC1,
RT286_DMIC2,
RT286_SPK_OUT,
RT286_HP_OUT,
};
#define RT286_POWER_REG_LEN ARRAY_SIZE(rt286_support_power_controls)
static int rt286_jack_detect(struct snd_soc_codec *codec, bool *hp, bool *mic)
{
struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
unsigned int val, buf;
int i;
*hp = false;
*mic = false;
if (rt286->pdata.cbj_en) {
buf = snd_soc_read(codec, RT286_GET_HP_SENSE);
*hp = buf & 0x80000000;
if (*hp) {
/* power on HV,VERF */
snd_soc_update_bits(codec,
RT286_POWER_CTRL1, 0x1001, 0x0);
/* power LDO1 */
snd_soc_update_bits(codec,
RT286_POWER_CTRL2, 0x4, 0x4);
snd_soc_write(codec, RT286_SET_MIC1, 0x24);
val = snd_soc_read(codec, RT286_CBJ_CTRL2);
msleep(200);
i = 40;
while (((val & 0x0800) == 0) && (i > 0)) {
val = snd_soc_read(codec,
RT286_CBJ_CTRL2);
i--;
msleep(20);
}
if (0x0400 == (val & 0x0700)) {
*mic = false;
snd_soc_write(codec,
RT286_SET_MIC1, 0x20);
/* power off HV,VERF */
snd_soc_update_bits(codec,
RT286_POWER_CTRL1, 0x1001, 0x1001);
snd_soc_update_bits(codec,
RT286_A_BIAS_CTRL3, 0xc000, 0x0000);
snd_soc_update_bits(codec,
RT286_CBJ_CTRL1, 0x0030, 0x0000);
snd_soc_update_bits(codec,
RT286_A_BIAS_CTRL2, 0xc000, 0x0000);
} else if ((0x0200 == (val & 0x0700)) ||
(0x0100 == (val & 0x0700))) {
*mic = true;
snd_soc_update_bits(codec,
RT286_A_BIAS_CTRL3, 0xc000, 0x8000);
snd_soc_update_bits(codec,
RT286_CBJ_CTRL1, 0x0030, 0x0020);
snd_soc_update_bits(codec,
RT286_A_BIAS_CTRL2, 0xc000, 0x8000);
} else {
*mic = false;
}
snd_soc_update_bits(codec,
RT286_MISC_CTRL1,
0x0060, 0x0000);
} else {
snd_soc_update_bits(codec,
RT286_MISC_CTRL1,
0x0060, 0x0020);
snd_soc_update_bits(codec,
RT286_A_BIAS_CTRL3,
0xc000, 0x8000);
snd_soc_update_bits(codec,
RT286_CBJ_CTRL1,
0x0030, 0x0020);
snd_soc_update_bits(codec,
RT286_A_BIAS_CTRL2,
0xc000, 0x8000);
*mic = false;
}
} else {
buf = snd_soc_read(codec, RT286_GET_HP_SENSE);
*hp = buf & 0x80000000;
buf = snd_soc_read(codec, RT286_GET_MIC1_SENSE);
*mic = buf & 0x80000000;
}
return 0;
}
static void rt286_jack_detect_work(struct work_struct *work)
{
struct rt286_priv *rt286 =
container_of(work, struct rt286_priv, jack_detect_work.work);
int status = 0;
bool hp = false;
bool mic = false;
rt286_jack_detect(rt286->codec, &hp, &mic);
if (hp == true)
status |= SND_JACK_HEADPHONE;
if (mic == true)
status |= SND_JACK_MICROPHONE;
snd_soc_jack_report(rt286->jack, status,
SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
}
int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
{
struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
rt286->jack = jack;
/* Send an initial empty report */
snd_soc_jack_report(rt286->jack, 0,
SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
return 0;
}
EXPORT_SYMBOL_GPL(rt286_mic_detect);
static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6350, 50, 0);
static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
static const struct snd_kcontrol_new rt286_snd_controls[] = {
SOC_DOUBLE_R_TLV("DAC0 Playback Volume", RT286_DACL_GAIN,
RT286_DACR_GAIN, 0, 0x7f, 0, out_vol_tlv),
SOC_DOUBLE_R_TLV("ADC0 Capture Volume", RT286_ADCL_GAIN,
RT286_ADCR_GAIN, 0, 0x7f, 0, out_vol_tlv),
SOC_SINGLE_TLV("AMIC Volume", RT286_MIC_GAIN,
0, 0x3, 0, mic_vol_tlv),
SOC_DOUBLE_R("Speaker Playback Switch", RT286_SPOL_GAIN,
RT286_SPOR_GAIN, RT286_MUTE_SFT, 1, 1),
};
/* Digital Mixer */
static const struct snd_kcontrol_new rt286_front_mix[] = {
SOC_DAPM_SINGLE("DAC Switch", RT286_F_DAC_SWITCH,
RT286_MUTE_SFT, 1, 1),
SOC_DAPM_SINGLE("RECMIX Switch", RT286_F_RECMIX_SWITCH,
RT286_MUTE_SFT, 1, 1),
};
/* Analog Input Mixer */
static const struct snd_kcontrol_new rt286_rec_mix[] = {
SOC_DAPM_SINGLE("Mic1 Switch", RT286_REC_MIC_SWITCH,
RT286_MUTE_SFT, 1, 1),
SOC_DAPM_SINGLE("I2S Switch", RT286_REC_I2S_SWITCH,
RT286_MUTE_SFT, 1, 1),
SOC_DAPM_SINGLE("Line1 Switch", RT286_REC_LINE_SWITCH,
RT286_MUTE_SFT, 1, 1),
SOC_DAPM_SINGLE("Beep Switch", RT286_REC_BEEP_SWITCH,
RT286_MUTE_SFT, 1, 1),
};
static const struct snd_kcontrol_new spo_enable_control =
SOC_DAPM_SINGLE("Switch", RT286_SET_PIN_SPK,
RT286_SET_PIN_SFT, 1, 0);
static const struct snd_kcontrol_new hpol_enable_control =
SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT286_HPOL_GAIN,
RT286_MUTE_SFT, 1, 1);
static const struct snd_kcontrol_new hpor_enable_control =
SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT286_HPOR_GAIN,
RT286_MUTE_SFT, 1, 1);
/* ADC0 source */
static const char * const rt286_adc_src[] = {
"Mic", "RECMIX", "Dmic"
};
static const int rt286_adc_values[] = {
0, 4, 5,
};
static SOC_VALUE_ENUM_SINGLE_DECL(
rt286_adc0_enum, RT286_ADC0_MUX, RT286_ADC_SEL_SFT,
RT286_ADC_SEL_MASK, rt286_adc_src, rt286_adc_values);
static const struct snd_kcontrol_new rt286_adc0_mux =
SOC_DAPM_ENUM("ADC 0 source", rt286_adc0_enum);
static SOC_VALUE_ENUM_SINGLE_DECL(
rt286_adc1_enum, RT286_ADC1_MUX, RT286_ADC_SEL_SFT,
RT286_ADC_SEL_MASK, rt286_adc_src, rt286_adc_values);
static const struct snd_kcontrol_new rt286_adc1_mux =
SOC_DAPM_ENUM("ADC 1 source", rt286_adc1_enum);
static const char * const rt286_dac_src[] = {
"Front", "Surround"
};
/* HP-OUT source */
static SOC_ENUM_SINGLE_DECL(rt286_hpo_enum, RT286_HPO_MUX,
0, rt286_dac_src);
static const struct snd_kcontrol_new rt286_hpo_mux =
SOC_DAPM_ENUM("HPO source", rt286_hpo_enum);
/* SPK-OUT source */
static SOC_ENUM_SINGLE_DECL(rt286_spo_enum, RT286_SPK_MUX,
0, rt286_dac_src);
static const struct snd_kcontrol_new rt286_spo_mux =
SOC_DAPM_ENUM("SPO source", rt286_spo_enum);
static int rt286_spk_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
switch (event) {
case SND_SOC_DAPM_POST_PMU:
snd_soc_write(codec,
RT286_SPK_EAPD, RT286_SET_EAPD_HIGH);
break;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_write(codec,
RT286_SPK_EAPD, RT286_SET_EAPD_LOW);
break;
default:
return 0;
}
return 0;
}
static int rt286_set_dmic1_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
switch (event) {
case SND_SOC_DAPM_POST_PMU:
snd_soc_write(codec, RT286_SET_PIN_DMIC1, 0x20);
break;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_write(codec, RT286_SET_PIN_DMIC1, 0);
break;
default:
return 0;
}
return 0;
}
static int rt286_adc_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event)
{
struct snd_soc_codec *codec = w->codec;
unsigned int nid;
nid = (w->reg >> 20) & 0xff;
switch (event) {
case SND_SOC_DAPM_POST_PMU:
snd_soc_update_bits(codec,
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0),
0x7080, 0x7000);
break;
case SND_SOC_DAPM_PRE_PMD:
snd_soc_update_bits(codec,
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, nid, 0),
0x7080, 0x7080);
break;
default:
return 0;
}
return 0;
}
static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = {
/* Input Lines */
SND_SOC_DAPM_INPUT("DMIC1 Pin"),
SND_SOC_DAPM_INPUT("DMIC2 Pin"),
SND_SOC_DAPM_INPUT("MIC1"),
SND_SOC_DAPM_INPUT("LINE1"),
SND_SOC_DAPM_INPUT("Beep"),
/* DMIC */
SND_SOC_DAPM_PGA_E("DMIC1", RT286_SET_POWER(RT286_DMIC1), 0, 1,
NULL, 0, rt286_set_dmic1_event,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_PGA("DMIC2", RT286_SET_POWER(RT286_DMIC2), 0, 1,
NULL, 0),
SND_SOC_DAPM_SUPPLY("DMIC Receiver", SND_SOC_NOPM,
0, 0, NULL, 0),
/* REC Mixer */
SND_SOC_DAPM_MIXER("RECMIX", SND_SOC_NOPM, 0, 0,
rt286_rec_mix, ARRAY_SIZE(rt286_rec_mix)),
/* ADCs */
SND_SOC_DAPM_ADC("ADC 0", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_ADC("ADC 1", NULL, SND_SOC_NOPM, 0, 0),
/* ADC Mux */
SND_SOC_DAPM_MUX_E("ADC 0 Mux", RT286_SET_POWER(RT286_ADC_IN1), 0, 1,
&rt286_adc0_mux, rt286_adc_event, SND_SOC_DAPM_PRE_PMD |
SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_MUX_E("ADC 1 Mux", RT286_SET_POWER(RT286_ADC_IN2), 0, 1,
&rt286_adc1_mux, rt286_adc_event, SND_SOC_DAPM_PRE_PMD |
SND_SOC_DAPM_POST_PMU),
/* Audio Interface */
SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_IN("AIF2RX", "AIF2 Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_AIF_OUT("AIF2TX", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
/* Output Side */
/* DACs */
SND_SOC_DAPM_DAC("DAC 0", NULL, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_DAC("DAC 1", NULL, SND_SOC_NOPM, 0, 0),
/* Output Mux */
SND_SOC_DAPM_MUX("SPK Mux", SND_SOC_NOPM, 0, 0, &rt286_spo_mux),
SND_SOC_DAPM_MUX("HPO Mux", SND_SOC_NOPM, 0, 0, &rt286_hpo_mux),
SND_SOC_DAPM_SUPPLY("HP Power", RT286_SET_PIN_HPO,
RT286_SET_PIN_SFT, 0, NULL, 0),
/* Output Mixer */
SND_SOC_DAPM_MIXER("Front", RT286_SET_POWER(RT286_DAC_OUT1), 0, 1,
rt286_front_mix, ARRAY_SIZE(rt286_front_mix)),
SND_SOC_DAPM_PGA("Surround", RT286_SET_POWER(RT286_DAC_OUT2), 0, 1,
NULL, 0),
/* Output Pga */
SND_SOC_DAPM_SWITCH_E("SPO", SND_SOC_NOPM, 0, 0,
&spo_enable_control, rt286_spk_event,
SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
SND_SOC_DAPM_SWITCH("HPO L", SND_SOC_NOPM, 0, 0,
&hpol_enable_control),
SND_SOC_DAPM_SWITCH("HPO R", SND_SOC_NOPM, 0, 0,
&hpor_enable_control),
/* Output Lines */
SND_SOC_DAPM_OUTPUT("SPOL"),
SND_SOC_DAPM_OUTPUT("SPOR"),
SND_SOC_DAPM_OUTPUT("HPO Pin"),
SND_SOC_DAPM_OUTPUT("SPDIF"),
};
static const struct snd_soc_dapm_route rt286_dapm_routes[] = {
{"DMIC1", NULL, "DMIC1 Pin"},
{"DMIC2", NULL, "DMIC2 Pin"},
{"DMIC1", NULL, "DMIC Receiver"},
{"DMIC2", NULL, "DMIC Receiver"},
{"RECMIX", "Beep Switch", "Beep"},
{"RECMIX", "Line1 Switch", "LINE1"},
{"RECMIX", "Mic1 Switch", "MIC1"},
{"ADC 0 Mux", "Dmic", "DMIC1"},
{"ADC 0 Mux", "RECMIX", "RECMIX"},
{"ADC 0 Mux", "Mic", "MIC1"},
{"ADC 1 Mux", "Dmic", "DMIC2"},
{"ADC 1 Mux", "RECMIX", "RECMIX"},
{"ADC 1 Mux", "Mic", "MIC1"},
{"ADC 0", NULL, "ADC 0 Mux"},
{"ADC 1", NULL, "ADC 1 Mux"},
{"AIF1TX", NULL, "ADC 0"},
{"AIF2TX", NULL, "ADC 1"},
{"DAC 0", NULL, "AIF1RX"},
{"DAC 1", NULL, "AIF2RX"},
{"Front", "DAC Switch", "DAC 0"},
{"Front", "RECMIX Switch", "RECMIX"},
{"Surround", NULL, "DAC 1"},
{"SPK Mux", "Front", "Front"},
{"SPK Mux", "Surround", "Surround"},
{"HPO Mux", "Front", "Front"},
{"HPO Mux", "Surround", "Surround"},
{"SPO", "Switch", "SPK Mux"},
{"HPO L", "Switch", "HPO Mux"},
{"HPO R", "Switch", "HPO Mux"},
{"HPO L", NULL, "HP Power"},
{"HPO R", NULL, "HP Power"},
{"SPOL", NULL, "SPO"},
{"SPOR", NULL, "SPO"},
{"HPO Pin", NULL, "HPO L"},
{"HPO Pin", NULL, "HPO R"},
};
static int rt286_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
unsigned int val = 0;
int d_len_code;
switch (params_rate(params)) {
/* bit 14 0:48K 1:44.1K */
case 44100:
val |= 0x4000;
break;
case 48000:
break;
default:
dev_err(codec->dev, "Unsupported sample rate %d\n",
params_rate(params));
return -EINVAL;
}
switch (rt286->sys_clk) {
case 12288000:
case 24576000:
if (params_rate(params) != 48000) {
dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
params_rate(params), rt286->sys_clk);
return -EINVAL;
}
break;
case 11289600:
case 22579200:
if (params_rate(params) != 44100) {
dev_err(codec->dev, "Sys_clk is not matched (%d %d)\n",
params_rate(params), rt286->sys_clk);
return -EINVAL;
}
break;
}
if (params_channels(params) <= 16) {
/* bit 3:0 Number of Channel */
val |= (params_channels(params) - 1);
} else {
dev_err(codec->dev, "Unsupported channels %d\n",
params_channels(params));
return -EINVAL;
}
d_len_code = 0;
switch (params_width(params)) {
/* bit 6:4 Bits per Sample */
case 16:
d_len_code = 0;
val |= (0x1 << 4);
break;
case 32:
d_len_code = 2;
val |= (0x4 << 4);
break;
case 20:
d_len_code = 1;
val |= (0x2 << 4);
break;
case 24:
d_len_code = 2;
val |= (0x3 << 4);
break;
case 8:
d_len_code = 3;
break;
default:
return -EINVAL;
}
snd_soc_update_bits(codec,
RT286_I2S_CTRL1, 0x0018, d_len_code << 3);
dev_dbg(codec->dev, "format val = 0x%x\n", val);
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
snd_soc_update_bits(codec, RT286_DAC_FORMAT, 0x407f, val);
else
snd_soc_update_bits(codec, RT286_ADC_FORMAT, 0x407f, val);
return 0;
}
static int rt286_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
struct snd_soc_codec *codec = dai->codec;
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
snd_soc_update_bits(codec,
RT286_I2S_CTRL1, 0x800, 0x800);
break;
case SND_SOC_DAIFMT_CBS_CFS:
snd_soc_update_bits(codec,
RT286_I2S_CTRL1, 0x800, 0x0);
break;
default:
return -EINVAL;
}
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
snd_soc_update_bits(codec,
RT286_I2S_CTRL1, 0x300, 0x0);
break;
case SND_SOC_DAIFMT_LEFT_J:
snd_soc_update_bits(codec,
RT286_I2S_CTRL1, 0x300, 0x1 << 8);
break;
case SND_SOC_DAIFMT_DSP_A:
snd_soc_update_bits(codec,
RT286_I2S_CTRL1, 0x300, 0x2 << 8);
break;
case SND_SOC_DAIFMT_DSP_B:
snd_soc_update_bits(codec,
RT286_I2S_CTRL1, 0x300, 0x3 << 8);
break;
default:
return -EINVAL;
}
/* bit 15 Stream Type 0:PCM 1:Non-PCM */
snd_soc_update_bits(codec, RT286_DAC_FORMAT, 0x8000, 0);
snd_soc_update_bits(codec, RT286_ADC_FORMAT, 0x8000, 0);
return 0;
}
static int rt286_set_dai_sysclk(struct snd_soc_dai *dai,
int clk_id, unsigned int freq, int dir)
{
struct snd_soc_codec *codec = dai->codec;
struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
dev_dbg(codec->dev, "%s freq=%d\n", __func__, freq);
if (RT286_SCLK_S_MCLK == clk_id) {
snd_soc_update_bits(codec,
RT286_I2S_CTRL2, 0x0100, 0x0);
snd_soc_update_bits(codec,
RT286_PLL_CTRL1, 0x20, 0x20);
} else {
snd_soc_update_bits(codec,
RT286_I2S_CTRL2, 0x0100, 0x0100);
snd_soc_update_bits(codec,
RT286_PLL_CTRL, 0x4, 0x4);
snd_soc_update_bits(codec,
RT286_PLL_CTRL1, 0x20, 0x0);
}
switch (freq) {
case 19200000:
if (RT286_SCLK_S_MCLK == clk_id) {
dev_err(codec->dev, "Should not use MCLK\n");
return -EINVAL;
}
snd_soc_update_bits(codec,
RT286_I2S_CTRL2, 0x40, 0x40);
break;
case 24000000:
if (RT286_SCLK_S_MCLK == clk_id) {
dev_err(codec->dev, "Should not use MCLK\n");
return -EINVAL;
}
snd_soc_update_bits(codec,
RT286_I2S_CTRL2, 0x40, 0x0);
break;
case 12288000:
case 11289600:
snd_soc_update_bits(codec,
RT286_I2S_CTRL2, 0x8, 0x0);
snd_soc_update_bits(codec,
RT286_CLK_DIV, 0xfc1e, 0x0004);
break;
case 24576000:
case 22579200:
snd_soc_update_bits(codec,
RT286_I2S_CTRL2, 0x8, 0x8);
snd_soc_update_bits(codec,
RT286_CLK_DIV, 0xfc1e, 0x5406);
break;
default:
dev_err(codec->dev, "Unsupported system clock\n");
return -EINVAL;
}
rt286->sys_clk = freq;
return 0;
}
static int rt286_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
{
struct snd_soc_codec *codec = dai->codec;
dev_dbg(codec->dev, "%s ratio=%d\n", __func__, ratio);
if (50 == ratio)
snd_soc_update_bits(codec,
RT286_I2S_CTRL1, 0x1000, 0x1000);
else
snd_soc_update_bits(codec,
RT286_I2S_CTRL1, 0x1000, 0x0);
return 0;
}
static int rt286_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
switch (level) {
case SND_SOC_BIAS_PREPARE:
if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) {
snd_soc_write(codec,
RT286_SET_AUDIO_POWER, AC_PWRST_D0);
snd_soc_update_bits(codec,
RT286_DC_GAIN, 0x200, 0x200);
}
break;
case SND_SOC_BIAS_ON:
mdelay(10);
break;
case SND_SOC_BIAS_STANDBY:
snd_soc_write(codec,
RT286_SET_AUDIO_POWER, AC_PWRST_D3);
snd_soc_update_bits(codec,
RT286_DC_GAIN, 0x200, 0x0);
break;
default:
break;
}
codec->dapm.bias_level = level;
return 0;
}
static irqreturn_t rt286_irq(int irq, void *data)
{
struct rt286_priv *rt286 = data;
bool hp = false;
bool mic = false;
int status = 0;
rt286_jack_detect(rt286->codec, &hp, &mic);
/* Clear IRQ */
snd_soc_update_bits(rt286->codec,
RT286_IRQ_CTRL, 0x1, 0x1);
if (hp == true)
status |= SND_JACK_HEADPHONE;
if (mic == true)
status |= SND_JACK_MICROPHONE;
snd_soc_jack_report(rt286->jack, status,
SND_JACK_MICROPHONE | SND_JACK_HEADPHONE);
pm_wakeup_event(&rt286->i2c->dev, 300);
return IRQ_HANDLED;
}
static int rt286_probe(struct snd_soc_codec *codec)
{
struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
codec->dapm.bias_level = SND_SOC_BIAS_OFF;
rt286->codec = codec;
return 0;
}
static int rt286_remove(struct snd_soc_codec *codec)
{
struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
cancel_delayed_work_sync(&rt286->jack_detect_work);
return 0;
}
#ifdef CONFIG_PM
static int rt286_suspend(struct snd_soc_codec *codec)
{
struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
regcache_cache_only(rt286->regmap, true);
regcache_mark_dirty(rt286->regmap);
return 0;
}
static int rt286_resume(struct snd_soc_codec *codec)
{
struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
regcache_cache_only(rt286->regmap, false);
rt286_index_sync(codec);
regcache_sync(rt286->regmap);
return 0;
}
#else
#define rt286_suspend NULL
#define rt286_resume NULL
#endif
#define RT286_STEREO_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
#define RT286_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
static const struct snd_soc_dai_ops rt286_aif_dai_ops = {
.hw_params = rt286_hw_params,
.set_fmt = rt286_set_dai_fmt,
.set_sysclk = rt286_set_dai_sysclk,
.set_bclk_ratio = rt286_set_bclk_ratio,
};
static struct snd_soc_dai_driver rt286_dai[] = {
{
.name = "rt286-aif1",
.id = RT286_AIF1,
.playback = {
.stream_name = "AIF1 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = RT286_STEREO_RATES,
.formats = RT286_FORMATS,
},
.capture = {
.stream_name = "AIF1 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = RT286_STEREO_RATES,
.formats = RT286_FORMATS,
},
.ops = &rt286_aif_dai_ops,
.symmetric_rates = 1,
},
{
.name = "rt286-aif2",
.id = RT286_AIF2,
.playback = {
.stream_name = "AIF2 Playback",
.channels_min = 1,
.channels_max = 2,
.rates = RT286_STEREO_RATES,
.formats = RT286_FORMATS,
},
.capture = {
.stream_name = "AIF2 Capture",
.channels_min = 1,
.channels_max = 2,
.rates = RT286_STEREO_RATES,
.formats = RT286_FORMATS,
},
.ops = &rt286_aif_dai_ops,
.symmetric_rates = 1,
},
};
static struct snd_soc_codec_driver soc_codec_dev_rt286 = {
.probe = rt286_probe,
.remove = rt286_remove,
.suspend = rt286_suspend,
.resume = rt286_resume,
.set_bias_level = rt286_set_bias_level,
.idle_bias_off = true,
.controls = rt286_snd_controls,
.num_controls = ARRAY_SIZE(rt286_snd_controls),
.dapm_widgets = rt286_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(rt286_dapm_widgets),
.dapm_routes = rt286_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(rt286_dapm_routes),
};
static const struct regmap_config rt286_regmap = {
.reg_bits = 32,
.val_bits = 32,
.max_register = 0x02370100,
.volatile_reg = rt286_volatile_register,
.readable_reg = rt286_readable_register,
.reg_write = rt286_hw_write,
.reg_read = rt286_hw_read,
.cache_type = REGCACHE_RBTREE,
.reg_defaults = rt286_reg,
.num_reg_defaults = ARRAY_SIZE(rt286_reg),
};
static const struct i2c_device_id rt286_i2c_id[] = {
{"rt286", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, rt286_i2c_id);
static const struct acpi_device_id rt286_acpi_match[] = {
{ "INT343A", 0 },
{},
};
MODULE_DEVICE_TABLE(acpi, rt286_acpi_match);
static int rt286_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct rt286_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt286_priv *rt286;
int i, ret;
rt286 = devm_kzalloc(&i2c->dev, sizeof(*rt286),
GFP_KERNEL);
if (NULL == rt286)
return -ENOMEM;
rt286->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt286_regmap);
if (IS_ERR(rt286->regmap)) {
ret = PTR_ERR(rt286->regmap);
dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
ret);
return ret;
}
regmap_read(rt286->regmap,
RT286_GET_PARAM(AC_NODE_ROOT, AC_PAR_VENDOR_ID), &ret);
if (ret != RT286_VENDOR_ID) {
dev_err(&i2c->dev,
"Device with ID register %x is not rt286\n", ret);
return -ENODEV;
}
rt286->index_cache = rt286_index_def;
rt286->i2c = i2c;
i2c_set_clientdata(i2c, rt286);
if (pdata)
rt286->pdata = *pdata;
regmap_write(rt286->regmap, RT286_SET_AUDIO_POWER, AC_PWRST_D3);
for (i = 0; i < RT286_POWER_REG_LEN; i++)
regmap_write(rt286->regmap,
RT286_SET_POWER(rt286_support_power_controls[i]),
AC_PWRST_D1);
if (!rt286->pdata.cbj_en) {
regmap_write(rt286->regmap, RT286_CBJ_CTRL2, 0x0000);
regmap_write(rt286->regmap, RT286_MIC1_DET_CTRL, 0x0816);
regmap_write(rt286->regmap, RT286_MISC_CTRL1, 0x0000);
regmap_update_bits(rt286->regmap,
RT286_CBJ_CTRL1, 0xf000, 0xb000);
} else {
regmap_update_bits(rt286->regmap,
RT286_CBJ_CTRL1, 0xf000, 0x5000);
}
mdelay(10);
if (!rt286->pdata.gpio2_en)
regmap_write(rt286->regmap, RT286_SET_DMIC2_DEFAULT, 0x4000);
else
regmap_write(rt286->regmap, RT286_SET_DMIC2_DEFAULT, 0);
mdelay(10);
/*Power down LDO2*/
regmap_update_bits(rt286->regmap, RT286_POWER_CTRL2, 0x8, 0x0);
/*Set depop parameter*/
regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL2, 0x403a, 0x401a);
regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL3, 0xf777, 0x4737);
regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL4, 0x00ff, 0x003f);
if (rt286->i2c->irq) {
regmap_update_bits(rt286->regmap,
RT286_IRQ_CTRL, 0x2, 0x2);
INIT_DELAYED_WORK(&rt286->jack_detect_work,
rt286_jack_detect_work);
schedule_delayed_work(&rt286->jack_detect_work,
msecs_to_jiffies(1250));
ret = request_threaded_irq(rt286->i2c->irq, NULL, rt286_irq,
IRQF_TRIGGER_HIGH | IRQF_ONESHOT, "rt286", rt286);
if (ret != 0) {
dev_err(&i2c->dev,
"Failed to reguest IRQ: %d\n", ret);
return ret;
}
}
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt286,
rt286_dai, ARRAY_SIZE(rt286_dai));
return ret;
}
static int rt286_i2c_remove(struct i2c_client *i2c)
{
struct rt286_priv *rt286 = i2c_get_clientdata(i2c);
if (i2c->irq)
free_irq(i2c->irq, rt286);
snd_soc_unregister_codec(&i2c->dev);
return 0;
}
static struct i2c_driver rt286_i2c_driver = {
.driver = {
.name = "rt286",
.owner = THIS_MODULE,
.acpi_match_table = ACPI_PTR(rt286_acpi_match),
},
.probe = rt286_i2c_probe,
.remove = rt286_i2c_remove,
.id_table = rt286_i2c_id,
};
module_i2c_driver(rt286_i2c_driver);
MODULE_DESCRIPTION("ASoC RT286 driver");
MODULE_AUTHOR("Bard Liao <bardliao@realtek.com>");
MODULE_LICENSE("GPL");
/*
* rt286.h -- RT286 ALSA SoC audio driver
*
* Copyright 2011 Realtek Microelectronics
* Author: Johnny Hsu <johnnyhsu@realtek.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#ifndef __RT286_H__
#define __RT286_H__
#define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D)
#define RT286_AUDIO_FUNCTION_GROUP 0x01
#define RT286_DAC_OUT1 0x02
#define RT286_DAC_OUT2 0x03
#define RT286_ADC_IN1 0x09
#define RT286_ADC_IN2 0x08
#define RT286_MIXER_IN 0x0b
#define RT286_MIXER_OUT1 0x0c
#define RT286_MIXER_OUT2 0x0d
#define RT286_DMIC1 0x12
#define RT286_DMIC2 0x13
#define RT286_SPK_OUT 0x14
#define RT286_MIC1 0x18
#define RT286_LINE1 0x1a
#define RT286_BEEP 0x1d
#define RT286_SPDIF 0x1e
#define RT286_VENDOR_REGISTERS 0x20
#define RT286_HP_OUT 0x21
#define RT286_MIXER_IN1 0x22
#define RT286_MIXER_IN2 0x23
#define RT286_SET_PIN_SFT 6
#define RT286_SET_PIN_ENABLE 0x40
#define RT286_SET_PIN_DISABLE 0
#define RT286_SET_EAPD_HIGH 0x2
#define RT286_SET_EAPD_LOW 0
#define RT286_MUTE_SFT 7
/* Verb commands */
#define RT286_GET_PARAM(NID, PARAM) VERB_CMD(AC_VERB_PARAMETERS, NID, PARAM)
#define RT286_SET_POWER(NID) VERB_CMD(AC_VERB_SET_POWER_STATE, NID, 0)
#define RT286_SET_AUDIO_POWER RT286_SET_POWER(RT286_AUDIO_FUNCTION_GROUP)
#define RT286_SET_HPO_POWER RT286_SET_POWER(RT286_HP_OUT)
#define RT286_SET_SPK_POWER RT286_SET_POWER(RT286_SPK_OUT)
#define RT286_SET_DMIC1_POWER RT286_SET_POWER(RT286_DMIC1)
#define RT286_SPK_MUX\
VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_SPK_OUT, 0)
#define RT286_HPO_MUX\
VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_HP_OUT, 0)
#define RT286_ADC0_MUX\
VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_MIXER_IN1, 0)
#define RT286_ADC1_MUX\
VERB_CMD(AC_VERB_SET_CONNECT_SEL, RT286_MIXER_IN2, 0)
#define RT286_SET_MIC1\
VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_MIC1, 0)
#define RT286_SET_PIN_HPO\
VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_HP_OUT, 0)
#define RT286_SET_PIN_SPK\
VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_SPK_OUT, 0)
#define RT286_SET_PIN_DMIC1\
VERB_CMD(AC_VERB_SET_PIN_WIDGET_CONTROL, RT286_DMIC1, 0)
#define RT286_SPK_EAPD\
VERB_CMD(AC_VERB_SET_EAPD_BTLENABLE, RT286_SPK_OUT, 0)
#define RT286_SET_AMP_GAIN_HPO\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0)
#define RT286_SET_AMP_GAIN_ADC_IN1\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0)
#define RT286_SET_AMP_GAIN_ADC_IN2\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN2, 0)
#define RT286_GET_HP_SENSE\
VERB_CMD(AC_VERB_GET_PIN_SENSE, RT286_HP_OUT, 0)
#define RT286_GET_MIC1_SENSE\
VERB_CMD(AC_VERB_GET_PIN_SENSE, RT286_MIC1, 0)
#define RT286_SET_DMIC2_DEFAULT\
VERB_CMD(AC_VERB_SET_CONFIG_DEFAULT_BYTES_3, RT286_DMIC2, 0)
#define RT286_DACL_GAIN\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_DAC_OUT1, 0xa000)
#define RT286_DACR_GAIN\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_DAC_OUT1, 0x9000)
#define RT286_ADCL_GAIN\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0x6000)
#define RT286_ADCR_GAIN\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_ADC_IN1, 0x5000)
#define RT286_MIC_GAIN\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIC1, 0x7000)
#define RT286_SPOL_GAIN\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_SPK_OUT, 0xa000)
#define RT286_SPOR_GAIN\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_SPK_OUT, 0x9000)
#define RT286_HPOL_GAIN\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0xa000)
#define RT286_HPOR_GAIN\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_HP_OUT, 0x9000)
#define RT286_F_DAC_SWITCH\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_OUT1, 0x7000)
#define RT286_F_RECMIX_SWITCH\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_OUT1, 0x7100)
#define RT286_REC_MIC_SWITCH\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7000)
#define RT286_REC_I2S_SWITCH\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7100)
#define RT286_REC_LINE_SWITCH\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7200)
#define RT286_REC_BEEP_SWITCH\
VERB_CMD(AC_VERB_SET_AMP_GAIN_MUTE, RT286_MIXER_IN, 0x7300)
#define RT286_DAC_FORMAT\
VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT286_DAC_OUT1, 0)
#define RT286_ADC_FORMAT\
VERB_CMD(AC_VERB_SET_STREAM_FORMAT, RT286_ADC_IN1, 0)
#define RT286_COEF_INDEX\
VERB_CMD(AC_VERB_SET_COEF_INDEX, RT286_VENDOR_REGISTERS, 0)
#define RT286_PROC_COEF\
VERB_CMD(AC_VERB_SET_PROC_COEF, RT286_VENDOR_REGISTERS, 0)
/* Index registers */
#define RT286_A_BIAS_CTRL1 0x01
#define RT286_A_BIAS_CTRL2 0x02
#define RT286_POWER_CTRL1 0x03
#define RT286_A_BIAS_CTRL3 0x04
#define RT286_POWER_CTRL2 0x08
#define RT286_I2S_CTRL1 0x09
#define RT286_I2S_CTRL2 0x0a
#define RT286_CLK_DIV 0x0b
#define RT286_DC_GAIN 0x0d
#define RT286_POWER_CTRL3 0x0f
#define RT286_MIC1_DET_CTRL 0x19
#define RT286_MISC_CTRL1 0x20
#define RT286_IRQ_CTRL 0x33
#define RT286_PLL_CTRL1 0x49
#define RT286_CBJ_CTRL1 0x4f
#define RT286_CBJ_CTRL2 0x50
#define RT286_PLL_CTRL 0x63
#define RT286_DEPOP_CTRL1 0x66
#define RT286_DEPOP_CTRL2 0x67
#define RT286_DEPOP_CTRL3 0x68
#define RT286_DEPOP_CTRL4 0x69
/* SPDIF (0x06) */
#define RT286_SPDIF_SEL_SFT 0
#define RT286_SPDIF_SEL_PCM0 0
#define RT286_SPDIF_SEL_PCM1 1
#define RT286_SPDIF_SEL_SPOUT 2
#define RT286_SPDIF_SEL_PP 3
/* RECMIX (0x0b) */
#define RT286_M_REC_BEEP_SFT 0
#define RT286_M_REC_LINE1_SFT 1
#define RT286_M_REC_MIC1_SFT 2
#define RT286_M_REC_I2S_SFT 3
/* Front (0x0c) */
#define RT286_M_FRONT_DAC_SFT 0
#define RT286_M_FRONT_REC_SFT 1
/* SPK-OUT (0x14) */
#define RT286_M_SPK_MUX_SFT 14
#define RT286_SPK_SEL_MASK 0x1
#define RT286_SPK_SEL_SFT 0
#define RT286_SPK_SEL_F 0
#define RT286_SPK_SEL_S 1
/* HP-OUT (0x21) */
#define RT286_M_HP_MUX_SFT 14
#define RT286_HP_SEL_MASK 0x1
#define RT286_HP_SEL_SFT 0
#define RT286_HP_SEL_F 0
#define RT286_HP_SEL_S 1
/* ADC (0x22) (0x23) */
#define RT286_ADC_SEL_MASK 0x7
#define RT286_ADC_SEL_SFT 0
#define RT286_ADC_SEL_SURR 0
#define RT286_ADC_SEL_FRONT 1
#define RT286_ADC_SEL_DMIC 2
#define RT286_ADC_SEL_BEEP 4
#define RT286_ADC_SEL_LINE1 5
#define RT286_ADC_SEL_I2S 6
#define RT286_ADC_SEL_MIC1 7
#define RT286_SCLK_S_MCLK 0
#define RT286_SCLK_S_PLL 1
enum {
RT286_AIF1,
RT286_AIF2,
RT286_AIFS,
};
int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
#endif /* __RT286_H__ */
......@@ -58,3 +58,15 @@ config SND_SOC_INTEL_BYT_MAX98090_MACH
help
This adds audio driver for Intel Baytrail platform based boards
with the MAX98090 audio codec.
config SND_SOC_INTEL_BROADWELL_MACH
tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint"
depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC
select SND_SOC_INTEL_HASWELL
select SND_COMPRESS_OFFLOAD
select SND_SOC_RT286
help
This adds support for the Wilcatpoint Audio DSP on Intel(R) Broadwell
Ultrabook platforms.
Say Y if you have such a device
If unsure select "N".
......@@ -24,7 +24,9 @@ obj-$(CONFIG_SND_SOC_INTEL_BAYTRAIL) += snd-soc-sst-baytrail-pcm.o
snd-soc-sst-haswell-objs := haswell.o
snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o
snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o
snd-soc-sst-broadwell-objs := broadwell.o
obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o
obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o
/*
* Intel Broadwell Wildcatpoint SST Audio
*
* Copyright (C) 2013, Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*/
#include <linux/module.h>
#include <linux/platform_device.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/soc.h>
#include <sound/pcm_params.h>
#include "sst-dsp.h"
#include "sst-haswell-ipc.h"
#include "../codecs/rt286.h"
static const struct snd_soc_dapm_widget broadwell_widgets[] = {
SND_SOC_DAPM_HP("Headphones", NULL),
SND_SOC_DAPM_SPK("Speaker", NULL),
SND_SOC_DAPM_MIC("Mic Jack", NULL),
SND_SOC_DAPM_MIC("DMIC1", NULL),
SND_SOC_DAPM_MIC("DMIC2", NULL),
SND_SOC_DAPM_LINE("Line Jack", NULL),
};
static const struct snd_soc_dapm_route broadwell_rt286_map[] = {
/* speaker */
{"Speaker", NULL, "SPOR"},
{"Speaker", NULL, "SPOL"},
/* HP jack connectors - unknown if we have jack deteck */
{"Headphones", NULL, "HPO Pin"},
/* other jacks */
{"MIC1", NULL, "Mic Jack"},
{"LINE1", NULL, "Line Jack"},
/* digital mics */
{"DMIC1 Pin", NULL, "DMIC1"},
{"DMIC2 Pin", NULL, "DMIC2"},
/* CODEC BE connections */
{"SSP0 CODEC IN", NULL, "AIF1 Capture"},
{"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
};
static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_pcm_hw_params *params)
{
struct snd_interval *rate = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_RATE);
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
/* The ADSP will covert the FE rate to 48k, stereo */
rate->min = rate->max = 48000;
channels->min = channels->max = 2;
/* set SSP0 to 16 bit */
snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
SNDRV_PCM_HW_PARAM_FIRST_MASK],
SNDRV_PCM_FORMAT_S16_LE);
return 0;
}
static int broadwell_rt286_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
int ret;
ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000,
SND_SOC_CLOCK_IN);
if (ret < 0) {
dev_err(rtd->dev, "can't set codec sysclk configuration\n");
return ret;
}
return ret;
}
static struct snd_soc_ops broadwell_rt286_ops = {
.hw_params = broadwell_rt286_hw_params,
};
static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_dapm_context *dapm = &codec->dapm;
struct sst_pdata *pdata = dev_get_platdata(rtd->platform->dev);
struct sst_hsw *broadwell = pdata->dsp;
int ret;
/* Set ADSP SSP port settings */
ret = sst_hsw_device_set_config(broadwell, SST_HSW_DEVICE_SSP_0,
SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
SST_HSW_DEVICE_CLOCK_MASTER, 9);
if (ret < 0) {
dev_err(rtd->dev, "error: failed to set device config\n");
return ret;
}
/* always connected - check HP for jack detect */
snd_soc_dapm_enable_pin(dapm, "Headphones");
snd_soc_dapm_enable_pin(dapm, "Speaker");
snd_soc_dapm_enable_pin(dapm, "Mic Jack");
snd_soc_dapm_enable_pin(dapm, "Line Jack");
snd_soc_dapm_enable_pin(dapm, "DMIC1");
snd_soc_dapm_enable_pin(dapm, "DMIC2");
return 0;
}
/* broadwell digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link broadwell_rt286_dais[] = {
/* Front End DAI links */
{
.name = "System PCM",
.stream_name = "System Playback",
.cpu_dai_name = "System Pin",
.platform_name = "haswell-pcm-audio",
.dynamic = 1,
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.init = broadwell_rtd_init,
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_playback = 1,
},
{
.name = "Offload0",
.stream_name = "Offload0 Playback",
.cpu_dai_name = "Offload0 Pin",
.platform_name = "haswell-pcm-audio",
.dynamic = 1,
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_playback = 1,
},
{
.name = "Offload1",
.stream_name = "Offload1 Playback",
.cpu_dai_name = "Offload1 Pin",
.platform_name = "haswell-pcm-audio",
.dynamic = 1,
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_playback = 1,
},
{
.name = "Loopback PCM",
.stream_name = "Loopback",
.cpu_dai_name = "Loopback Pin",
.platform_name = "haswell-pcm-audio",
.dynamic = 0,
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_capture = 1,
},
{
.name = "Capture PCM",
.stream_name = "Capture",
.cpu_dai_name = "Capture Pin",
.platform_name = "haswell-pcm-audio",
.dynamic = 1,
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_capture = 1,
},
/* Back End DAI links */
{
/* SSP0 - Codec */
.name = "Codec",
.be_id = 0,
.cpu_dai_name = "snd-soc-dummy-dai",
.platform_name = "snd-soc-dummy",
.no_pcm = 1,
.codec_name = "i2c-INT343A:00",
.codec_dai_name = "rt286-aif1",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
SND_SOC_DAIFMT_CBS_CFS,
.ignore_suspend = 1,
.ignore_pmdown_time = 1,
.be_hw_params_fixup = broadwell_ssp0_fixup,
.ops = &broadwell_rt286_ops,
.dpcm_playback = 1,
.dpcm_capture = 1,
},
};
/* broadwell audio machine driver for WPT + RT286S */
static struct snd_soc_card broadwell_rt286 = {
.name = "broadwell-rt286",
.owner = THIS_MODULE,
.dai_link = broadwell_rt286_dais,
.num_links = ARRAY_SIZE(broadwell_rt286_dais),
.dapm_widgets = broadwell_widgets,
.num_dapm_widgets = ARRAY_SIZE(broadwell_widgets),
.dapm_routes = broadwell_rt286_map,
.num_dapm_routes = ARRAY_SIZE(broadwell_rt286_map),
.fully_routed = true,
};
static int broadwell_audio_probe(struct platform_device *pdev)
{
broadwell_rt286.dev = &pdev->dev;
return snd_soc_register_card(&broadwell_rt286);
}
static int broadwell_audio_remove(struct platform_device *pdev)
{
snd_soc_unregister_card(&broadwell_rt286);
return 0;
}
static struct platform_driver broadwell_audio = {
.probe = broadwell_audio_probe,
.remove = broadwell_audio_remove,
.driver = {
.name = "broadwell-audio",
.owner = THIS_MODULE,
},
};
module_platform_driver(broadwell_audio)
/* Module information */
MODULE_AUTHOR("Liam Girdwood, Xingchao Wang");
MODULE_DESCRIPTION("Intel SST Audio for WPT/Broadwell");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:broadwell-audio");
......@@ -63,14 +63,6 @@ static struct snd_soc_jack_pin hs_jack_pins[] = {
.pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE,
},
{
.pin = "Ext Spk",
.mask = SND_JACK_LINEOUT,
},
{
.pin = "Int Mic",
.mask = SND_JACK_LINEIN,
},
};
static struct snd_soc_jack_gpio hs_jack_gpios[] = {
......
......@@ -34,6 +34,7 @@ static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
};
static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = {
{"Headset Mic", NULL, "MICBIAS1"},
{"IN2P", NULL, "Headset Mic"},
{"IN2N", NULL, "Headset Mic"},
{"DMIC1", NULL, "Internal Mic"},
......
/*
* Copyright (C) 2013-14 Intel Corp
* Author: Ramesh Babu <ramesh.babu.koul@intel.com>
* Omair M Abdullah <omair.m.abdullah@intel.com>
* Samreen Nilofer <samreen.nilofer@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.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
*/
#ifndef __SST_CONTROLS_V2_H__
#define __SST_CONTROLS_V2_H__
enum {
MERR_DPCM_AUDIO = 0,
MERR_DPCM_COMPR,
};
#endif
......@@ -122,6 +122,26 @@ struct sst_byt_tstamp {
u32 channel_peak[8];
} __packed;
struct sst_byt_fw_version {
u8 build;
u8 minor;
u8 major;
u8 type;
} __packed;
struct sst_byt_fw_build_info {
u8 date[16];
u8 time[16];
} __packed;
struct sst_byt_fw_init {
struct sst_byt_fw_version fw_version;
struct sst_byt_fw_build_info build_info;
u16 result;
u8 module_id;
u8 debug_info;
} __packed;
/* driver internal IPC message structure */
struct ipc_message {
struct list_head list;
......@@ -868,6 +888,7 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
{
struct sst_byt *byt;
struct sst_fw *byt_sst_fw;
struct sst_byt_fw_init init;
int err;
dev_dbg(dev, "initialising Byt DSP IPC\n");
......@@ -929,6 +950,15 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
goto boot_err;
}
/* show firmware information */
sst_dsp_inbox_read(byt->dsp, &init, sizeof(init));
dev_info(byt->dev, "FW version: %02x.%02x.%02x.%02x\n",
init.fw_version.major, init.fw_version.minor,
init.fw_version.build, init.fw_version.type);
dev_info(byt->dev, "Build type: %x\n", init.fw_version.type);
dev_info(byt->dev, "Build date: %s %s\n",
init.build_info.date, init.build_info.time);
pdata->dsp = byt;
byt->fw = byt_sst_fw;
......
......@@ -224,19 +224,23 @@ EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64);
void sst_dsp_dump(struct sst_dsp *sst)
{
sst->ops->dump(sst);
if (sst->ops->dump)
sst->ops->dump(sst);
}
EXPORT_SYMBOL_GPL(sst_dsp_dump);
void sst_dsp_reset(struct sst_dsp *sst)
{
sst->ops->reset(sst);
if (sst->ops->reset)
sst->ops->reset(sst);
}
EXPORT_SYMBOL_GPL(sst_dsp_reset);
int sst_dsp_boot(struct sst_dsp *sst)
{
sst->ops->boot(sst);
if (sst->ops->boot)
sst->ops->boot(sst);
return 0;
}
EXPORT_SYMBOL_GPL(sst_dsp_boot);
......
......@@ -52,7 +52,11 @@
#define SST_CLKCTL 0x78
#define SST_CSR2 0x80
#define SST_LTRC 0xE0
#define SST_HDMC 0xE8
#define SST_HMDC 0xE8
#define SST_SHIM_BEGIN SST_CSR
#define SST_SHIM_END SST_HDMC
#define SST_DBGO 0xF0
#define SST_SHIM_SIZE 0x100
......@@ -73,6 +77,8 @@
#define SST_CSR_S0IOCS (0x1 << 21)
#define SST_CSR_S1IOCS (0x1 << 23)
#define SST_CSR_LPCS (0x1 << 31)
#define SST_CSR_24MHZ_LPCS (SST_CSR_SBCS0 | SST_CSR_SBCS1 | SST_CSR_LPCS)
#define SST_CSR_24MHZ_NO_LPCS (SST_CSR_SBCS0 | SST_CSR_SBCS1)
#define SST_BYT_CSR_RST (0x1 << 0)
#define SST_BYT_CSR_VECTOR_SEL (0x1 << 1)
#define SST_BYT_CSR_STALL (0x1 << 2)
......@@ -92,6 +98,14 @@
#define SST_IMRX_DONE (0x1 << 0)
#define SST_BYT_IMRX_REQUEST (0x1 << 1)
/* IMRD / IMD */
#define SST_IMRD_DONE (0x1 << 0)
#define SST_IMRD_BUSY (0x1 << 1)
#define SST_IMRD_SSP0 (0x1 << 16)
#define SST_IMRD_DMAC0 (0x1 << 21)
#define SST_IMRD_DMAC1 (0x1 << 22)
#define SST_IMRD_DMAC (SST_IMRD_DMAC0 | SST_IMRD_DMAC1)
/* IPCX / IPCC */
#define SST_IPCX_DONE (0x1 << 30)
#define SST_IPCX_BUSY (0x1 << 31)
......@@ -118,9 +132,21 @@
/* LTRC */
#define SST_LTRC_VAL(x) (x << 0)
/* HDMC */
#define SST_HDMC_HDDA0(x) (x << 0)
#define SST_HDMC_HDDA1(x) (x << 7)
/* HMDC */
#define SST_HMDC_HDDA0(x) (x << 0)
#define SST_HMDC_HDDA1(x) (x << 7)
#define SST_HMDC_HDDA_E0_CH0 1
#define SST_HMDC_HDDA_E0_CH1 2
#define SST_HMDC_HDDA_E0_CH2 4
#define SST_HMDC_HDDA_E0_CH3 8
#define SST_HMDC_HDDA_E1_CH0 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH0)
#define SST_HMDC_HDDA_E1_CH1 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH1)
#define SST_HMDC_HDDA_E1_CH2 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH2)
#define SST_HMDC_HDDA_E1_CH3 SST_HMDC_HDDA1(SST_HMDC_HDDA_E0_CH3)
#define SST_HMDC_HDDA_E0_ALLCH (SST_HMDC_HDDA_E0_CH0 | SST_HMDC_HDDA_E0_CH1 | \
SST_HMDC_HDDA_E0_CH2 | SST_HMDC_HDDA_E0_CH3)
#define SST_HMDC_HDDA_E1_ALLCH (SST_HMDC_HDDA_E1_CH0 | SST_HMDC_HDDA_E1_CH1 | \
SST_HMDC_HDDA_E1_CH2 | SST_HMDC_HDDA_E1_CH3)
/* SST Vendor Defined Registers and bits */
......@@ -130,11 +156,16 @@
#define SST_VDRTCTL3 0xaC
/* VDRTCTL0 */
#define SST_VDRTCL0_APLLSE_MASK 1
#define SST_VDRTCL0_DSRAMPGE_SHIFT 16
#define SST_VDRTCL0_DSRAMPGE_MASK (0xffff << SST_VDRTCL0_DSRAMPGE_SHIFT)
#define SST_VDRTCL0_ISRAMPGE_SHIFT 6
#define SST_VDRTCL0_ISRAMPGE_MASK (0x3ff << SST_VDRTCL0_ISRAMPGE_SHIFT)
/* PMCS */
#define SST_PMCS 0x84
#define SST_PMCS_PS_MASK 0x3
struct sst_dsp;
/*
......
......@@ -28,9 +28,6 @@
#include <linux/firmware.h>
#include <linux/pm_runtime.h>
#include <linux/acpi.h>
#include <acpi/acpi_bus.h>
#include "sst-dsp.h"
#include "sst-dsp-priv.h"
#include "sst-haswell-ipc.h"
......@@ -272,9 +269,9 @@ static void hsw_boot(struct sst_dsp *sst)
SST_CSR2_SDFD_SSP1);
/* enable DMA engine 0,1 all channels to access host memory */
sst_dsp_shim_update_bits_unlocked(sst, SST_HDMC,
SST_HDMC_HDDA1(0xff) | SST_HDMC_HDDA0(0xff),
SST_HDMC_HDDA1(0xff) | SST_HDMC_HDDA0(0xff));
sst_dsp_shim_update_bits_unlocked(sst, SST_HMDC,
SST_HMDC_HDDA1(0xff) | SST_HMDC_HDDA0(0xff),
SST_HMDC_HDDA1(0xff) | SST_HMDC_HDDA0(0xff));
/* disable all clock gating */
writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL2);
......@@ -313,9 +310,7 @@ static const struct sst_adsp_memregion lp_region[] = {
/* wild cat point ADSP mem regions */
static const struct sst_adsp_memregion wpt_region[] = {
{0x00000, 0x40000, 8, SST_MEM_DRAM}, /* D-SRAM0 - 8 * 32kB */
{0x40000, 0x80000, 8, SST_MEM_DRAM}, /* D-SRAM1 - 8 * 32kB */
{0x80000, 0xA0000, 4, SST_MEM_DRAM}, /* D-SRAM2 - 4 * 32kB */
{0x00000, 0xA0000, 20, SST_MEM_DRAM}, /* D-SRAM0,D-SRAM1,D-SRAM2 - 20 * 32kB */
{0xA0000, 0xF0000, 10, SST_MEM_IRAM}, /* I-SRAM - 10 * 32kB */
};
......@@ -339,21 +334,40 @@ static int hsw_acpi_resource_map(struct sst_dsp *sst, struct sst_pdata *pdata)
return 0;
}
struct sst_sram_shift {
u32 dev_id; /* SST Device IDs */
u32 iram_shift;
u32 dram_shift;
};
static const struct sst_sram_shift sram_shift[] = {
{SST_DEV_ID_LYNX_POINT, 6, 16}, /* lp */
{SST_DEV_ID_WILDCAT_POINT, 2, 12}, /* wpt */
};
static u32 hsw_block_get_bit(struct sst_mem_block *block)
{
u32 bit = 0, shift = 0;
u32 bit = 0, shift = 0, index;
struct sst_dsp *sst = block->dsp;
switch (block->type) {
case SST_MEM_DRAM:
shift = 16;
break;
case SST_MEM_IRAM:
shift = 6;
break;
default:
return 0;
for (index = 0; index < ARRAY_SIZE(sram_shift); index++) {
if (sram_shift[index].dev_id == sst->id)
break;
}
if (index < ARRAY_SIZE(sram_shift)) {
switch (block->type) {
case SST_MEM_DRAM:
shift = sram_shift[index].dram_shift;
break;
case SST_MEM_IRAM:
shift = sram_shift[index].iram_shift;
break;
default:
shift = 0;
}
} else
shift = 0;
bit = 1 << (block->index + shift);
return bit;
......@@ -501,8 +515,9 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
}
}
/* set default power gating mask */
writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL0);
/* set default power gating control, enable power gating control for all blocks. that is,
can't be accessed, please enable each block before accessing. */
writel(0xffffffff, sst->addr.pci_cfg + SST_VDRTCTL0);
return 0;
}
......
......@@ -183,7 +183,7 @@ struct sst_hsw_ipc_fw_ready {
u32 inbox_size;
u32 outbox_size;
u32 fw_info_size;
u8 fw_info[1];
u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)];
} __attribute__((packed));
struct ipc_message {
......@@ -457,9 +457,10 @@ static void ipc_tx_msgs(struct kthread_work *work)
return;
}
/* if the DSP is busy we will TX messages after IRQ */
/* if the DSP is busy, we will TX messages after IRQ.
* also postpone if we are in the middle of procesing completion irq*/
ipcx = sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX);
if (ipcx & SST_IPCX_BUSY) {
if (ipcx & (SST_IPCX_BUSY | SST_IPCX_DONE)) {
spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
return;
}
......@@ -502,6 +503,7 @@ static int tx_wait_done(struct sst_hsw *hsw, struct ipc_message *msg,
ipc_shim_dbg(hsw, "message timeout");
trace_ipc_error("error message timeout for", msg->header);
list_del(&msg->list);
ret = -ETIMEDOUT;
} else {
......@@ -569,6 +571,9 @@ static void hsw_fw_ready(struct sst_hsw *hsw, u32 header)
{
struct sst_hsw_ipc_fw_ready fw_ready;
u32 offset;
u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)];
char *tmp[5], *pinfo;
int i = 0;
offset = (header & 0x1FFFFFFF) << 3;
......@@ -589,6 +594,19 @@ static void hsw_fw_ready(struct sst_hsw *hsw, u32 header)
fw_ready.inbox_offset, fw_ready.inbox_size);
dev_dbg(hsw->dev, " mailbox downstream 0x%x - size 0x%x\n",
fw_ready.outbox_offset, fw_ready.outbox_size);
if (fw_ready.fw_info_size < sizeof(fw_ready.fw_info)) {
fw_ready.fw_info[fw_ready.fw_info_size] = 0;
dev_dbg(hsw->dev, " Firmware info: %s \n", fw_ready.fw_info);
/* log the FW version info got from the mailbox here. */
memcpy(fw_info, fw_ready.fw_info, fw_ready.fw_info_size);
pinfo = &fw_info[0];
for (i = 0; i < sizeof(tmp) / sizeof(char *); i++)
tmp[i] = strsep(&pinfo, " ");
dev_info(hsw->dev, "FW loaded, mailbox readback FW info: type %s, - "
"version: %s.%s, build %s, source commit id: %s\n",
tmp[0], tmp[1], tmp[2], tmp[3], tmp[4]);
}
}
static void hsw_notification_work(struct work_struct *work)
......@@ -671,7 +689,9 @@ static void hsw_stream_update(struct sst_hsw *hsw, struct ipc_message *msg)
switch (stream_msg) {
case IPC_STR_STAGE_MESSAGE:
case IPC_STR_NOTIFICATION:
break;
case IPC_STR_RESET:
trace_ipc_notification("stream reset", stream->reply.stream_hw_id);
break;
case IPC_STR_PAUSE:
stream->running = false;
......@@ -762,7 +782,8 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header)
}
/* update any stream states */
hsw_stream_update(hsw, msg);
if (msg_get_global_type(header) == IPC_GLB_STREAM_MESSAGE)
hsw_stream_update(hsw, msg);
/* wake up and return the error if we have waiters on this message ? */
list_del(&msg->list);
......@@ -1628,7 +1649,7 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw,
enum sst_hsw_dx_state state, struct sst_hsw_ipc_dx_reply *dx)
{
u32 header, state_;
int ret;
int ret, item;
header = IPC_GLB_TYPE(IPC_GLB_ENTER_DX_STATE);
state_ = state;
......@@ -1642,6 +1663,13 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw,
return ret;
}
for (item = 0; item < dx->entries_no; item++) {
dev_dbg(hsw->dev,
"Item[%d] offset[%x] - size[%x] - source[%x]\n",
item, dx->mem_info[item].offset,
dx->mem_info[item].size,
dx->mem_info[item].source);
}
dev_dbg(hsw->dev, "ipc: got %d entry numbers for state %d\n",
dx->entries_no, state);
......@@ -1775,8 +1803,6 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
/* get the FW version */
sst_hsw_fw_get_version(hsw, &version);
dev_info(hsw->dev, "FW loaded: type %d - version: %d.%d build %d\n",
version.type, version.major, version.minor, version.build);
/* get the globalmixer */
ret = sst_hsw_mixer_get_info(hsw);
......
......@@ -3,7 +3,7 @@
/*
* sst_mfld_dsp.h - Intel SST Driver for audio engine
*
* Copyright (C) 2008-12 Intel Corporation
* Copyright (C) 2008-14 Intel Corporation
* Authors: Vinod Koul <vinod.koul@linux.intel.com>
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
......@@ -19,6 +19,142 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
#define SST_MAX_BIN_BYTES 1024
#define MAX_DBG_RW_BYTES 80
#define MAX_NUM_SCATTER_BUFFERS 8
#define MAX_LOOP_BACK_DWORDS 8
/* IPC base address and mailbox, timestamp offsets */
#define SST_MAILBOX_SIZE 0x0400
#define SST_MAILBOX_SEND 0x0000
#define SST_TIME_STAMP 0x1800
#define SST_TIME_STAMP_MRFLD 0x800
#define SST_RESERVED_OFFSET 0x1A00
#define SST_SCU_LPE_MAILBOX 0x1000
#define SST_LPE_SCU_MAILBOX 0x1400
#define SST_SCU_LPE_LOG_BUF (SST_SCU_LPE_MAILBOX+16)
#define PROCESS_MSG 0x80
/* Message ID's for IPC messages */
/* Bits B7: SST or IA/SC ; B6-B4: Msg Category; B3-B0: Msg Type */
/* I2L Firmware/Codec Download msgs */
#define IPC_IA_PREP_LIB_DNLD 0x01
#define IPC_IA_LIB_DNLD_CMPLT 0x02
#define IPC_IA_GET_FW_VERSION 0x04
#define IPC_IA_GET_FW_BUILD_INF 0x05
#define IPC_IA_GET_FW_INFO 0x06
#define IPC_IA_GET_FW_CTXT 0x07
#define IPC_IA_SET_FW_CTXT 0x08
#define IPC_IA_PREPARE_SHUTDOWN 0x31
/* I2L Codec Config/control msgs */
#define IPC_PREP_D3 0x10
#define IPC_IA_SET_CODEC_PARAMS 0x10
#define IPC_IA_GET_CODEC_PARAMS 0x11
#define IPC_IA_SET_PPP_PARAMS 0x12
#define IPC_IA_GET_PPP_PARAMS 0x13
#define IPC_SST_PERIOD_ELAPSED_MRFLD 0xA
#define IPC_IA_ALG_PARAMS 0x1A
#define IPC_IA_TUNING_PARAMS 0x1B
#define IPC_IA_SET_RUNTIME_PARAMS 0x1C
#define IPC_IA_SET_PARAMS 0x1
#define IPC_IA_GET_PARAMS 0x2
#define IPC_EFFECTS_CREATE 0xE
#define IPC_EFFECTS_DESTROY 0xF
/* I2L Stream config/control msgs */
#define IPC_IA_ALLOC_STREAM_MRFLD 0x2
#define IPC_IA_ALLOC_STREAM 0x20 /* Allocate a stream ID */
#define IPC_IA_FREE_STREAM_MRFLD 0x03
#define IPC_IA_FREE_STREAM 0x21 /* Free the stream ID */
#define IPC_IA_SET_STREAM_PARAMS 0x22
#define IPC_IA_SET_STREAM_PARAMS_MRFLD 0x12
#define IPC_IA_GET_STREAM_PARAMS 0x23
#define IPC_IA_PAUSE_STREAM 0x24
#define IPC_IA_PAUSE_STREAM_MRFLD 0x4
#define IPC_IA_RESUME_STREAM 0x25
#define IPC_IA_RESUME_STREAM_MRFLD 0x5
#define IPC_IA_DROP_STREAM 0x26
#define IPC_IA_DROP_STREAM_MRFLD 0x07
#define IPC_IA_DRAIN_STREAM 0x27 /* Short msg with str_id */
#define IPC_IA_DRAIN_STREAM_MRFLD 0x8
#define IPC_IA_CONTROL_ROUTING 0x29
#define IPC_IA_VTSV_UPDATE_MODULES 0x20
#define IPC_IA_VTSV_DETECTED 0x21
#define IPC_IA_START_STREAM_MRFLD 0X06
#define IPC_IA_START_STREAM 0x30 /* Short msg with str_id */
#define IPC_IA_SET_GAIN_MRFLD 0x21
/* Debug msgs */
#define IPC_IA_DBG_MEM_READ 0x40
#define IPC_IA_DBG_MEM_WRITE 0x41
#define IPC_IA_DBG_LOOP_BACK 0x42
#define IPC_IA_DBG_LOG_ENABLE 0x45
#define IPC_IA_DBG_SET_PROBE_PARAMS 0x47
/* L2I Firmware/Codec Download msgs */
#define IPC_IA_FW_INIT_CMPLT 0x81
#define IPC_IA_FW_INIT_CMPLT_MRFLD 0x01
#define IPC_IA_FW_ASYNC_ERR_MRFLD 0x11
/* L2I Codec Config/control msgs */
#define IPC_SST_FRAGMENT_ELPASED 0x90 /* Request IA more data */
#define IPC_SST_BUF_UNDER_RUN 0x92 /* PB Under run and stopped */
#define IPC_SST_BUF_OVER_RUN 0x93 /* CAP Under run and stopped */
#define IPC_SST_DRAIN_END 0x94 /* PB Drain complete and stopped */
#define IPC_SST_CHNGE_SSP_PARAMS 0x95 /* PB SSP parameters changed */
#define IPC_SST_STREAM_PROCESS_FATAL_ERR 0x96/* error in processing a stream */
#define IPC_SST_PERIOD_ELAPSED 0x97 /* period elapsed */
#define IPC_SST_ERROR_EVENT 0x99 /* Buffer over run occurred */
/* L2S messages */
#define IPC_SC_DDR_LINK_UP 0xC0
#define IPC_SC_DDR_LINK_DOWN 0xC1
#define IPC_SC_SET_LPECLK_REQ 0xC2
#define IPC_SC_SSP_BIT_BANG 0xC3
/* L2I Error reporting msgs */
#define IPC_IA_MEM_ALLOC_FAIL 0xE0
#define IPC_IA_PROC_ERR 0xE1 /* error in processing a
stream can be used by playback and
capture modules */
/* L2I Debug msgs */
#define IPC_IA_PRINT_STRING 0xF0
/* Buffer under-run */
#define IPC_IA_BUF_UNDER_RUN_MRFLD 0x0B
/* Mrfld specific defines:
* For asynchronous messages(INIT_CMPLT, PERIOD_ELAPSED, ASYNC_ERROR)
* received from FW, the format is:
* - IPC High: pvt_id is set to zero. Always short message.
* - msg_id is in lower 16-bits of IPC low payload.
* - pipe_id is in higher 16-bits of IPC low payload for period_elapsed.
* - error id is in higher 16-bits of IPC low payload for async errors.
*/
#define SST_ASYNC_DRV_ID 0
/* Command Response or Acknowledge message to any IPC message will have
* same message ID and stream ID information which is sent.
* There is no specific Ack message ID. The data field is used as response
* meaning.
*/
enum ackData {
IPC_ACK_SUCCESS = 0,
IPC_ACK_FAILURE,
};
enum ipc_ia_msg_id {
IPC_CMD = 1, /*!< Task Control message ID */
IPC_SET_PARAMS = 2,/*!< Task Set param message ID */
IPC_GET_PARAMS = 3, /*!< Task Get param message ID */
IPC_INVALID = 0xFF, /*!<Task Get param message ID */
};
enum sst_codec_types {
/* AUDIO/MUSIC CODEC Type Definitions */
SST_CODEC_TYPE_UNKNOWN = 0,
......@@ -35,14 +171,157 @@ enum stream_type {
SST_STREAM_TYPE_MUSIC = 1,
};
enum sst_error_codes {
/* Error code,response to msgId: Description */
/* Common error codes */
SST_SUCCESS = 0, /* Success */
SST_ERR_INVALID_STREAM_ID = 1,
SST_ERR_INVALID_MSG_ID = 2,
SST_ERR_INVALID_STREAM_OP = 3,
SST_ERR_INVALID_PARAMS = 4,
SST_ERR_INVALID_CODEC = 5,
SST_ERR_INVALID_MEDIA_TYPE = 6,
SST_ERR_STREAM_ERR = 7,
SST_ERR_STREAM_IN_USE = 15,
};
struct ipc_dsp_hdr {
u16 mod_index_id:8; /*!< DSP Command ID specific to tasks */
u16 pipe_id:8; /*!< instance of the module in the pipeline */
u16 mod_id; /*!< Pipe_id */
u16 cmd_id; /*!< Module ID = lpe_algo_types_t */
u16 length; /*!< Length of the payload only */
} __packed;
union ipc_header_high {
struct {
u32 msg_id:8; /* Message ID - Max 256 Message Types */
u32 task_id:4; /* Task ID associated with this comand */
u32 drv_id:4; /* Identifier for the driver to track*/
u32 rsvd1:8; /* Reserved */
u32 result:4; /* Reserved */
u32 res_rqd:1; /* Response rqd */
u32 large:1; /* Large Message if large = 1 */
u32 done:1; /* bit 30 - Done bit */
u32 busy:1; /* bit 31 - busy bit*/
} part;
u32 full;
} __packed;
/* IPC header */
union ipc_header_mrfld {
struct {
u32 header_low_payload;
union ipc_header_high header_high;
} p;
u64 full;
} __packed;
/* CAUTION NOTE: All IPC message body must be multiple of 32 bits.*/
/* IPC Header */
union ipc_header {
struct {
u32 msg_id:8; /* Message ID - Max 256 Message Types */
u32 str_id:5;
u32 large:1; /* Large Message if large = 1 */
u32 reserved:2; /* Reserved for future use */
u32 data:14; /* Ack/Info for msg, size of msg in Mailbox */
u32 done:1; /* bit 30 */
u32 busy:1; /* bit 31 */
} part;
u32 full;
} __packed;
/* Firmware build info */
struct sst_fw_build_info {
unsigned char date[16]; /* Firmware build date */
unsigned char time[16]; /* Firmware build time */
} __packed;
/* Firmware Version info */
struct snd_sst_fw_version {
u8 build; /* build number*/
u8 minor; /* minor number*/
u8 major; /* major number*/
u8 type; /* build type */
};
struct ipc_header_fw_init {
struct snd_sst_fw_version fw_version;/* Firmware version details */
struct sst_fw_build_info build_info;
u16 result; /* Fw init result */
u8 module_id; /* Module ID in case of error */
u8 debug_info; /* Debug info from Module ID in case of fail */
} __packed;
struct snd_sst_tstamp {
u64 ring_buffer_counter; /* PB/CP: Bytes copied from/to DDR. */
u64 hardware_counter; /* PB/CP: Bytes DMAed to/from SSP. */
u64 frames_decoded;
u64 bytes_decoded;
u64 bytes_copied;
u32 sampling_frequency;
u32 channel_peak[8];
} __packed;
/* Stream type params struture for Alloc stream */
struct snd_sst_str_type {
u8 codec_type; /* Codec type */
u8 str_type; /* 1 = voice 2 = music */
u8 operation; /* Playback or Capture */
u8 protected_str; /* 0=Non DRM, 1=DRM */
u8 time_slots;
u8 reserved; /* Reserved */
u16 result; /* Result used for acknowledgment */
} __packed;
/* Library info structure */
struct module_info {
u32 lib_version;
u32 lib_type;/*TBD- KLOCKWORK u8 lib_type;*/
u32 media_type;
u8 lib_name[12];
u32 lib_caps;
unsigned char b_date[16]; /* Lib build date */
unsigned char b_time[16]; /* Lib build time */
} __packed;
/* Library slot info */
struct lib_slot_info {
u8 slot_num; /* 1 or 2 */
u8 reserved1;
u16 reserved2;
u32 iram_size; /* slot size in IRAM */
u32 dram_size; /* slot size in DRAM */
u32 iram_offset; /* starting offset of slot in IRAM */
u32 dram_offset; /* starting offset of slot in DRAM */
} __packed;
struct snd_ppp_mixer_params {
__u32 type; /*Type of the parameter */
__u32 size;
__u32 input_stream_bitmap; /*Input stream Bit Map*/
} __packed;
struct snd_sst_lib_download {
struct module_info lib_info; /* library info type, capabilities etc */
struct lib_slot_info slot_info; /* slot info to be downloaded */
u32 mod_entry_pt;
};
struct snd_sst_lib_download_info {
struct snd_sst_lib_download dload_lib;
u16 result; /* Result used for acknowledgment */
u8 pvt_id; /* Private ID */
u8 reserved; /* for alignment */
};
struct snd_pcm_params {
u8 num_chan; /* 1=Mono, 2=Stereo */
u8 pcm_wd_sz; /* 16/24 - bit*/
u32 reserved; /* Bitrate in bits per second */
u32 sfreq; /* Sampling rate in Hz */
u8 use_offload_path;
u8 use_offload_path; /* 0-PCM using period elpased & ALSA interfaces
1-PCM stream via compressed interface */
u8 reserved2;
u16 reserved3;
u32 sfreq; /* Sampling rate in Hz */
u8 channel_map[8];
} __packed;
......@@ -76,6 +355,7 @@ struct snd_aac_params {
struct snd_wma_params {
u8 num_chan; /* 1=Mono, 2=Stereo */
u8 pcm_wd_sz; /* 16/24 - bit*/
u16 reserved1;
u32 brate; /* Use the hard coded value. */
u32 sfreq; /* Sampling freq eg. 8000, 441000, 48000 */
u32 channel_mask; /* Channel Mask */
......@@ -101,26 +381,153 @@ struct sst_address_info {
};
struct snd_sst_alloc_params_ext {
struct sst_address_info ring_buf_info[8];
u8 sg_count;
u8 reserved;
u16 reserved2;
u32 frag_size; /*Number of samples after which period elapsed
__u16 sg_count;
__u16 reserved;
__u32 frag_size; /*Number of samples after which period elapsed
message is sent valid only if path = 0*/
} __packed;
struct sst_address_info ring_buf_info[8];
};
struct snd_sst_stream_params {
union snd_sst_codec_params uc;
} __packed;
struct snd_sst_params {
u32 result;
u32 stream_id;
u8 codec;
u8 ops;
u8 stream_type;
u8 device_type;
u8 task;
struct snd_sst_stream_params sparams;
struct snd_sst_alloc_params_ext aparams;
};
struct snd_sst_alloc_mrfld {
u16 codec_type;
u8 operation;
u8 sg_count;
struct sst_address_info ring_buf_info[8];
u32 frag_size;
u32 ts;
struct snd_sst_stream_params codec_params;
} __packed;
/* Alloc stream params structure */
struct snd_sst_alloc_params {
struct snd_sst_str_type str_type;
struct snd_sst_stream_params stream_params;
struct snd_sst_alloc_params_ext alloc_params;
} __packed;
/* Alloc stream response message */
struct snd_sst_alloc_response {
struct snd_sst_str_type str_type; /* Stream type for allocation */
struct snd_sst_lib_download lib_dnld; /* Valid only for codec dnld */
};
/* Drop response */
struct snd_sst_drop_response {
u32 result;
u32 bytes;
};
struct snd_sst_async_msg {
u32 msg_id; /* Async msg id */
u32 payload[0];
};
struct snd_sst_async_err_msg {
u32 fw_resp; /* Firmware Result */
u32 lib_resp; /*Library result */
} __packed;
struct snd_sst_vol {
u32 stream_id;
s32 volume;
u32 ramp_duration;
u32 ramp_type; /* Ramp type, default=0 */
};
/* Gain library parameters for mrfld
* based on DSP command spec v0.82
*/
struct snd_sst_gain_v2 {
u16 gain_cell_num; /* num of gain cells to modify*/
u8 cell_nbr_idx; /* instance index*/
u8 cell_path_idx; /* pipe-id */
u16 module_id; /*module id */
u16 left_cell_gain; /* left gain value in dB*/
u16 right_cell_gain; /* right gain value in dB*/
u16 gain_time_const; /* gain time constant*/
} __packed;
struct snd_sst_mute {
u32 stream_id;
u32 mute;
};
struct snd_sst_runtime_params {
u8 type;
u8 str_id;
u8 size;
u8 rsvd;
void *addr;
} __packed;
enum stream_param_type {
SST_SET_TIME_SLOT = 0,
SST_SET_CHANNEL_INFO = 1,
OTHERS = 2, /*reserved for future params*/
};
/* CSV Voice call routing structure */
struct snd_sst_control_routing {
u8 control; /* 0=start, 1=Stop */
u8 reserved[3]; /* Reserved- for 32 bit alignment */
};
struct ipc_post {
struct list_head node;
union ipc_header header; /* driver specific */
bool is_large;
bool is_process_reply;
union ipc_header_mrfld mrfld_header;
char *mailbox_data;
};
struct snd_sst_ctxt_params {
u32 address; /* Physical Address in DDR where the context is stored */
u32 size; /* size of the context */
};
struct snd_sst_lpe_log_params {
u8 dbg_type;
u8 module_id;
u8 log_level;
u8 reserved;
} __packed;
enum snd_sst_bytes_type {
SND_SST_BYTES_SET = 0x1,
SND_SST_BYTES_GET = 0x2,
};
struct snd_sst_bytes_v2 {
u8 type;
u8 ipc_msg;
u8 block;
u8 task_id;
u8 pipe_id;
u8 rsvd;
u16 len;
char bytes[0];
};
#define MAX_VTSV_FILES 2
struct snd_sst_vtsv_info {
struct sst_address_info vfiles[MAX_VTSV_FILES];
} __packed;
#endif /* __SST_MFLD_DSP_H__ */
......@@ -100,14 +100,19 @@ static int sst_platform_compr_set_params(struct snd_compr_stream *cstream,
int retval;
struct snd_sst_params str_params;
struct sst_compress_cb cb;
struct snd_soc_pcm_runtime *rtd = cstream->private_data;
struct snd_soc_platform *platform = rtd->platform;
struct sst_data *ctx = snd_soc_platform_get_drvdata(platform);
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;
/* fill the device type and stream id to pass to SST driver */
retval = sst_fill_stream_params(cstream, ctx, &str_params, true);
pr_debug("compr_set_params: fill stream params ret_val = 0x%x\n", retval);
if (retval < 0)
return retval;
switch (params->codec.id) {
case SND_AUDIOCODEC_MP3: {
......
/*
* sst_mfld_platform.c - Intel MID Platform driver
*
* Copyright (C) 2010-2013 Intel Corp
* Copyright (C) 2010-2014 Intel Corp
* Author: Vinod Koul <vinod.koul@intel.com>
* Author: Harsha Priya <priya.harsha@intel.com>
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
......@@ -27,7 +27,9 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/compress_driver.h>
#include <asm/platform_sst_audio.h>
#include "sst-mfld-platform.h"
#include "sst-atom-controls.h"
struct sst_device *sst;
static DEFINE_MUTEX(sst_lock);
......@@ -92,6 +94,13 @@ static struct snd_pcm_hardware sst_platform_pcm_hw = {
.fifo_size = SST_FIFO_SIZE,
};
static struct sst_dev_stream_map dpcm_strm_map[] = {
{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, /* Reserved, not in use */
{MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA1_IN, SST_TASK_ID_MEDIA, 0},
{MERR_DPCM_COMPR, 0, SNDRV_PCM_STREAM_PLAYBACK, PIPE_MEDIA0_IN, SST_TASK_ID_MEDIA, 0},
{MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PCM1_OUT, SST_TASK_ID_MEDIA, 0},
};
/* MFLD - MSIC */
static struct snd_soc_dai_driver sst_platform_dai[] = {
{
......@@ -143,58 +152,142 @@ static inline int sst_get_stream_status(struct sst_runtime_stream *stream)
return state;
}
static void sst_fill_alloc_params(struct snd_pcm_substream *substream,
struct snd_sst_alloc_params_ext *alloc_param)
{
unsigned int channels;
snd_pcm_uframes_t period_size;
ssize_t periodbytes;
ssize_t buffer_bytes = snd_pcm_lib_buffer_bytes(substream);
u32 buffer_addr = virt_to_phys(substream->dma_buffer.area);
channels = substream->runtime->channels;
period_size = substream->runtime->period_size;
periodbytes = samples_to_bytes(substream->runtime, period_size);
alloc_param->ring_buf_info[0].addr = buffer_addr;
alloc_param->ring_buf_info[0].size = buffer_bytes;
alloc_param->sg_count = 1;
alloc_param->reserved = 0;
alloc_param->frag_size = periodbytes * channels;
}
static void sst_fill_pcm_params(struct snd_pcm_substream *substream,
struct sst_pcm_params *param)
struct snd_sst_stream_params *param)
{
param->uc.pcm_params.num_chan = (u8) substream->runtime->channels;
param->uc.pcm_params.pcm_wd_sz = substream->runtime->sample_bits;
param->uc.pcm_params.sfreq = substream->runtime->rate;
/* PCM stream via ALSA interface */
param->uc.pcm_params.use_offload_path = 0;
param->uc.pcm_params.reserved2 = 0;
memset(param->uc.pcm_params.channel_map, 0, sizeof(u8));
param->num_chan = (u8) substream->runtime->channels;
param->pcm_wd_sz = substream->runtime->sample_bits;
param->reserved = 0;
param->sfreq = substream->runtime->rate;
param->ring_buffer_size = snd_pcm_lib_buffer_bytes(substream);
param->period_count = substream->runtime->period_size;
param->ring_buffer_addr = virt_to_phys(substream->dma_buffer.area);
pr_debug("period_cnt = %d\n", param->period_count);
pr_debug("sfreq= %d, wd_sz = %d\n", param->sfreq, param->pcm_wd_sz);
}
static int sst_platform_alloc_stream(struct snd_pcm_substream *substream)
static int sst_get_stream_mapping(int dev, int sdev, int dir,
struct sst_dev_stream_map *map, int size)
{
int i;
if (map == NULL)
return -EINVAL;
/* index 0 is not used in stream map */
for (i = 1; i < size; i++) {
if ((map[i].dev_num == dev) && (map[i].direction == dir))
return i;
}
return 0;
}
int sst_fill_stream_params(void *substream,
const struct sst_data *ctx, struct snd_sst_params *str_params, bool is_compress)
{
int map_size;
int index;
struct sst_dev_stream_map *map;
struct snd_pcm_substream *pstream = NULL;
struct snd_compr_stream *cstream = NULL;
map = ctx->pdata->pdev_strm_map;
map_size = ctx->pdata->strm_map_size;
if (is_compress == true)
cstream = (struct snd_compr_stream *)substream;
else
pstream = (struct snd_pcm_substream *)substream;
str_params->stream_type = SST_STREAM_TYPE_MUSIC;
/* For pcm streams */
if (pstream) {
index = sst_get_stream_mapping(pstream->pcm->device,
pstream->number, pstream->stream,
map, map_size);
if (index <= 0)
return -EINVAL;
str_params->stream_id = index;
str_params->device_type = map[index].device_id;
str_params->task = map[index].task_id;
str_params->ops = (u8)pstream->stream;
}
if (cstream) {
index = sst_get_stream_mapping(cstream->device->device,
0, cstream->direction,
map, map_size);
if (index <= 0)
return -EINVAL;
str_params->stream_id = index;
str_params->device_type = map[index].device_id;
str_params->task = map[index].task_id;
str_params->ops = (u8)cstream->direction;
}
return 0;
}
static int sst_platform_alloc_stream(struct snd_pcm_substream *substream,
struct snd_soc_platform *platform)
{
struct sst_runtime_stream *stream =
substream->runtime->private_data;
struct sst_pcm_params param = {0};
struct sst_stream_params str_params = {0};
int ret_val;
struct snd_sst_stream_params param = {{{0,},},};
struct snd_sst_params str_params = {0};
struct snd_sst_alloc_params_ext alloc_params = {0};
int ret_val = 0;
struct sst_data *ctx = snd_soc_platform_get_drvdata(platform);
/* set codec params and inform SST driver the same */
sst_fill_pcm_params(substream, &param);
sst_fill_alloc_params(substream, &alloc_params);
substream->runtime->dma_area = substream->dma_buffer.area;
str_params.sparams = param;
str_params.codec = param.codec;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
str_params.ops = STREAM_OPS_PLAYBACK;
str_params.device_type = substream->pcm->device + 1;
pr_debug("Playbck stream,Device %d\n",
substream->pcm->device);
} else {
str_params.ops = STREAM_OPS_CAPTURE;
str_params.device_type = SND_SST_DEVICE_CAPTURE;
pr_debug("Capture stream,Device %d\n",
substream->pcm->device);
}
ret_val = stream->ops->open(&str_params);
pr_debug("SST_SND_PLAY/CAPTURE ret_val = %x\n", ret_val);
str_params.aparams = alloc_params;
str_params.codec = SST_CODEC_TYPE_PCM;
/* fill the device type and stream id to pass to SST driver */
ret_val = sst_fill_stream_params(substream, ctx, &str_params, false);
if (ret_val < 0)
return ret_val;
stream->stream_info.str_id = ret_val;
pr_debug("str id : %d\n", stream->stream_info.str_id);
stream->stream_info.str_id = str_params.stream_id;
ret_val = stream->ops->open(&str_params);
if (ret_val <= 0)
return ret_val;
return ret_val;
}
static void sst_period_elapsed(void *mad_substream)
static void sst_period_elapsed(void *arg)
{
struct snd_pcm_substream *substream = mad_substream;
struct snd_pcm_substream *substream = arg;
struct sst_runtime_stream *stream;
int status;
......@@ -218,7 +311,7 @@ static int sst_platform_init_stream(struct snd_pcm_substream *substream)
pr_debug("setting buffer ptr param\n");
sst_set_stream_status(stream, SST_PLATFORM_INIT);
stream->stream_info.period_elapsed = sst_period_elapsed;
stream->stream_info.mad_substream = substream;
stream->stream_info.arg = substream;
stream->stream_info.buffer_ptr = 0;
stream->stream_info.sfreq = substream->runtime->rate;
ret_val = stream->ops->device_control(
......@@ -230,19 +323,12 @@ static int sst_platform_init_stream(struct snd_pcm_substream *substream)
}
/* end -- helper functions */
static int sst_platform_open(struct snd_pcm_substream *substream)
static int sst_media_open(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
int ret_val = 0;
struct snd_pcm_runtime *runtime = substream->runtime;
struct sst_runtime_stream *stream;
int ret_val;
pr_debug("sst_platform_open called\n");
snd_soc_set_runtime_hwparams(substream, &sst_platform_pcm_hw);
ret_val = snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
if (ret_val < 0)
return ret_val;
stream = kzalloc(sizeof(*stream), GFP_KERNEL);
if (!stream)
......@@ -251,50 +337,69 @@ static int sst_platform_open(struct snd_pcm_substream *substream)
/* get the sst ops */
mutex_lock(&sst_lock);
if (!sst) {
if (!sst ||
!try_module_get(sst->dev->driver->owner)) {
pr_err("no device available to run\n");
mutex_unlock(&sst_lock);
kfree(stream);
return -ENODEV;
}
if (!try_module_get(sst->dev->driver->owner)) {
mutex_unlock(&sst_lock);
kfree(stream);
return -ENODEV;
ret_val = -ENODEV;
goto out_ops;
}
stream->ops = sst->ops;
mutex_unlock(&sst_lock);
stream->stream_info.str_id = 0;
sst_set_stream_status(stream, SST_PLATFORM_INIT);
stream->stream_info.mad_substream = substream;
stream->stream_info.arg = substream;
/* allocate memory for SST API set */
runtime->private_data = stream;
return 0;
/* Make sure, that the period size is always even */
snd_pcm_hw_constraint_step(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_PERIODS, 2);
return snd_pcm_hw_constraint_integer(runtime,
SNDRV_PCM_HW_PARAM_PERIODS);
out_ops:
kfree(stream);
mutex_unlock(&sst_lock);
return ret_val;
}
static int sst_platform_close(struct snd_pcm_substream *substream)
static void sst_media_close(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct sst_runtime_stream *stream;
int ret_val = 0, str_id;
pr_debug("sst_platform_close called\n");
stream = substream->runtime->private_data;
str_id = stream->stream_info.str_id;
if (str_id)
ret_val = stream->ops->close(str_id);
module_put(sst->dev->driver->owner);
kfree(stream);
return ret_val;
}
static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream)
static inline unsigned int get_current_pipe_id(struct snd_soc_platform *platform,
struct snd_pcm_substream *substream)
{
struct sst_data *sst = snd_soc_platform_get_drvdata(platform);
struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map;
struct sst_runtime_stream *stream =
substream->runtime->private_data;
u32 str_id = stream->stream_info.str_id;
unsigned int pipe_id;
pipe_id = map[str_id].device_id;
pr_debug("%s: got pipe_id = %#x for str_id = %d\n",
__func__, pipe_id, str_id);
return pipe_id;
}
static int sst_media_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct sst_runtime_stream *stream;
int ret_val = 0, str_id;
pr_debug("sst_platform_pcm_prepare called\n");
stream = substream->runtime->private_data;
str_id = stream->stream_info.str_id;
if (stream->stream_info.str_id) {
......@@ -303,8 +408,8 @@ static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream)
return ret_val;
}
ret_val = sst_platform_alloc_stream(substream);
if (ret_val < 0)
ret_val = sst_platform_alloc_stream(substream, dai->platform);
if (ret_val <= 0)
return ret_val;
snprintf(substream->pcm->id, sizeof(substream->pcm->id),
"%d", stream->stream_info.str_id);
......@@ -316,6 +421,41 @@ static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream)
return ret_val;
}
static int sst_media_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params,
struct snd_soc_dai *dai)
{
snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
memset(substream->runtime->dma_area, 0, params_buffer_bytes(params));
return 0;
}
static int sst_media_hw_free(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
return snd_pcm_lib_free_pages(substream);
}
static struct snd_soc_dai_ops sst_media_dai_ops = {
.startup = sst_media_open,
.shutdown = sst_media_close,
.prepare = sst_media_prepare,
.hw_params = sst_media_hw_params,
.hw_free = sst_media_hw_free,
};
static int sst_platform_open(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime;
if (substream->pcm->internal)
return 0;
runtime = substream->runtime;
runtime->hw = sst_platform_pcm_hw;
return 0;
}
static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
int cmd)
{
......@@ -331,7 +471,7 @@ static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
pr_debug("sst: Trigger Start\n");
str_cmd = SST_SND_START;
status = SST_PLATFORM_RUNNING;
stream->stream_info.mad_substream = substream;
stream->stream_info.arg = substream;
break;
case SNDRV_PCM_TRIGGER_STOP:
pr_debug("sst: in stop\n");
......@@ -377,32 +517,15 @@ static snd_pcm_uframes_t sst_platform_pcm_pointer
pr_err("sst: error code = %d\n", ret_val);
return ret_val;
}
return stream->stream_info.buffer_ptr;
}
static int sst_platform_pcm_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
memset(substream->runtime->dma_area, 0, params_buffer_bytes(params));
return 0;
}
static int sst_platform_pcm_hw_free(struct snd_pcm_substream *substream)
{
return snd_pcm_lib_free_pages(substream);
substream->runtime->delay = str_info->pcm_delay;
return str_info->buffer_ptr;
}
static struct snd_pcm_ops sst_platform_ops = {
.open = sst_platform_open,
.close = sst_platform_close,
.ioctl = snd_pcm_lib_ioctl,
.prepare = sst_platform_pcm_prepare,
.trigger = sst_platform_pcm_trigger,
.pointer = sst_platform_pcm_pointer,
.hw_params = sst_platform_pcm_hw_params,
.hw_free = sst_platform_pcm_hw_free,
};
static void sst_pcm_free(struct snd_pcm *pcm)
......@@ -413,15 +536,15 @@ static void sst_pcm_free(struct snd_pcm *pcm)
static int sst_pcm_new(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_dai *dai = rtd->cpu_dai;
struct snd_pcm *pcm = rtd->pcm;
int retval = 0;
pr_debug("sst_pcm_new called\n");
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream ||
pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
if (dai->driver->playback.channels_min ||
dai->driver->capture.channels_min) {
retval = snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data(GFP_KERNEL),
snd_dma_continuous_data(GFP_DMA),
SST_MIN_BUFFER, SST_MAX_BUFFER);
if (retval) {
pr_err("dma buffer allocationf fail\n");
......@@ -445,10 +568,28 @@ static const struct snd_soc_component_driver sst_component = {
static int sst_platform_probe(struct platform_device *pdev)
{
struct sst_data *drv;
int ret;
struct sst_platform_data *pdata;
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL);
if (drv == NULL) {
pr_err("kzalloc failed\n");
return -ENOMEM;
}
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
if (pdata == NULL) {
pr_err("kzalloc failed for pdata\n");
return -ENOMEM;
}
pdata->pdev_strm_map = dpcm_strm_map;
pdata->strm_map_size = ARRAY_SIZE(dpcm_strm_map);
drv->pdata = pdata;
mutex_init(&drv->lock);
dev_set_drvdata(&pdev->dev, drv);
pr_debug("sst_platform_probe called\n");
sst = NULL;
ret = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv);
if (ret) {
pr_err("registering soc platform failed\n");
......
......@@ -39,9 +39,10 @@ extern struct sst_device *sst;
struct pcm_stream_info {
int str_id;
void *mad_substream;
void (*period_elapsed) (void *mad_substream);
void *arg;
void (*period_elapsed) (void *arg);
unsigned long long buffer_ptr;
unsigned long long pcm_delay;
int sfreq;
};
......@@ -62,7 +63,9 @@ enum sst_controls {
SST_SND_BUFFER_POINTER = 0x05,
SST_SND_STREAM_INIT = 0x06,
SST_SND_START = 0x07,
SST_MAX_CONTROLS = 0x07,
SST_SET_BYTE_STREAM = 0x100A,
SST_GET_BYTE_STREAM = 0x100B,
SST_MAX_CONTROLS = SST_GET_BYTE_STREAM,
};
enum sst_stream_ops {
......@@ -124,8 +127,9 @@ struct compress_sst_ops {
};
struct sst_ops {
int (*open) (struct sst_stream_params *str_param);
int (*open) (struct snd_sst_params *str_param);
int (*device_control) (int cmd, void *arg);
int (*set_generic_params)(enum sst_controls cmd, void *arg);
int (*close) (unsigned int str_id);
};
......@@ -143,10 +147,27 @@ struct sst_device {
char *name;
struct device *dev;
struct sst_ops *ops;
struct platform_device *pdev;
struct compress_sst_ops *compr_ops;
};
struct sst_data;
void sst_set_stream_status(struct sst_runtime_stream *stream, int state);
int sst_fill_stream_params(void *substream, const struct sst_data *ctx,
struct snd_sst_params *str_params, bool is_compress);
struct sst_algo_int_control_v2 {
struct soc_mixer_control mc;
u16 module_id; /* module identifieer */
u16 pipe_id; /* location info: pipe_id + instance_id */
u16 instance_id;
unsigned int value; /* Value received is stored here */
};
struct sst_data {
struct platform_device *pdev;
struct sst_platform_data *pdata;
struct mutex lock;
};
int sst_register_dsp(struct sst_device *sst);
int sst_unregister_dsp(struct sst_device *sst);
#endif
config SND_KIRKWOOD_SOC
tristate "SoC Audio for the Marvell Kirkwood and Dove chips"
depends on ARCH_KIRKWOOD || ARCH_DOVE || ARCH_MVEBU || MACH_KIRKWOOD || COMPILE_TEST
depends on ARCH_DOVE || ARCH_MVEBU || COMPILE_TEST
help
Say Y or M if you want to add support for codecs attached to
the Kirkwood I2S interface. You will also need to select the
......@@ -15,20 +15,3 @@ config SND_KIRKWOOD_SOC_ARMADA370_DB
Say Y if you want to add support for SoC audio on
the Armada 370 Development Board.
config SND_KIRKWOOD_SOC_OPENRD
tristate "SoC Audio support for Kirkwood Openrd Client"
depends on SND_KIRKWOOD_SOC && (MACH_OPENRD_CLIENT || MACH_OPENRD_ULTIMATE || COMPILE_TEST)
depends on I2C
select SND_SOC_CS42L51
help
Say Y if you want to add support for SoC audio on
Openrd Client.
config SND_KIRKWOOD_SOC_T5325
tristate "SoC Audio support for HP t5325"
depends on SND_KIRKWOOD_SOC && (MACH_T5325 || COMPILE_TEST) && I2C
select SND_SOC_ALC5623
help
Say Y if you want to add support for SoC audio on
the HP t5325 thin client.
......@@ -2,10 +2,6 @@ snd-soc-kirkwood-objs := kirkwood-dma.o kirkwood-i2s.o
obj-$(CONFIG_SND_KIRKWOOD_SOC) += snd-soc-kirkwood.o
snd-soc-openrd-objs := kirkwood-openrd.o
snd-soc-t5325-objs := kirkwood-t5325.o
snd-soc-armada-370-db-objs := armada-370-db.o
obj-$(CONFIG_SND_KIRKWOOD_SOC_ARMADA370_DB) += snd-soc-armada-370-db.o
obj-$(CONFIG_SND_KIRKWOOD_SOC_OPENRD) += snd-soc-openrd.o
obj-$(CONFIG_SND_KIRKWOOD_SOC_T5325) += snd-soc-t5325.o
......@@ -28,11 +28,12 @@ static struct kirkwood_dma_data *kirkwood_priv(struct snd_pcm_substream *subs)
}
static struct snd_pcm_hardware kirkwood_dma_snd_hw = {
.info = (SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_PAUSE),
.info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
.buffer_bytes_max = KIRKWOOD_SND_MAX_BUFFER_BYTES,
.period_bytes_min = KIRKWOOD_SND_MIN_PERIOD_BYTES,
.period_bytes_max = KIRKWOOD_SND_MAX_PERIOD_BYTES,
......
......@@ -212,7 +212,8 @@ static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
KIRKWOOD_PLAYCTL_SIZE_MASK);
priv->ctl_play |= ctl_play;
} else {
priv->ctl_rec &= ~KIRKWOOD_RECCTL_SIZE_MASK;
priv->ctl_rec &= ~(KIRKWOOD_RECCTL_ENABLE_MASK |
KIRKWOOD_RECCTL_SIZE_MASK);
priv->ctl_rec |= ctl_rec;
}
......@@ -221,14 +222,24 @@ static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
return 0;
}
static unsigned kirkwood_i2s_play_mute(unsigned ctl)
{
if (!(ctl & KIRKWOOD_PLAYCTL_I2S_EN))
ctl |= KIRKWOOD_PLAYCTL_I2S_MUTE;
if (!(ctl & KIRKWOOD_PLAYCTL_SPDIF_EN))
ctl |= KIRKWOOD_PLAYCTL_SPDIF_MUTE;
return ctl;
}
static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai)
{
struct snd_pcm_runtime *runtime = substream->runtime;
struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
uint32_t ctl, value;
ctl = readl(priv->io + KIRKWOOD_PLAYCTL);
if (ctl & KIRKWOOD_PLAYCTL_PAUSE) {
if ((ctl & KIRKWOOD_PLAYCTL_ENABLE_MASK) == 0) {
unsigned timeout = 5000;
/*
* The Armada510 spec says that if we enter pause mode, the
......@@ -256,14 +267,16 @@ static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
ctl &= ~KIRKWOOD_PLAYCTL_SPDIF_EN; /* i2s */
else
ctl &= ~KIRKWOOD_PLAYCTL_I2S_EN; /* spdif */
ctl = kirkwood_i2s_play_mute(ctl);
value = ctl & ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
writel(value, priv->io + KIRKWOOD_PLAYCTL);
/* enable interrupts */
value = readl(priv->io + KIRKWOOD_INT_MASK);
value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES;
writel(value, priv->io + KIRKWOOD_INT_MASK);
if (!runtime->no_period_wakeup) {
value = readl(priv->io + KIRKWOOD_INT_MASK);
value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES;
writel(value, priv->io + KIRKWOOD_INT_MASK);
}
/* enable playback */
writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
......@@ -295,6 +308,7 @@ static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
KIRKWOOD_PLAYCTL_SPDIF_MUTE);
ctl = kirkwood_i2s_play_mute(ctl);
writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
break;
......@@ -322,8 +336,7 @@ static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream,
else
ctl &= ~KIRKWOOD_RECCTL_I2S_EN; /* spdif */
value = ctl & ~(KIRKWOOD_RECCTL_I2S_EN |
KIRKWOOD_RECCTL_SPDIF_EN);
value = ctl & ~KIRKWOOD_RECCTL_ENABLE_MASK;
writel(value, priv->io + KIRKWOOD_RECCTL);
/* enable interrupts */
......@@ -347,7 +360,7 @@ static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream,
/* disable all records */
value = readl(priv->io + KIRKWOOD_RECCTL);
value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN);
value &= ~KIRKWOOD_RECCTL_ENABLE_MASK;
writel(value, priv->io + KIRKWOOD_RECCTL);
break;
......@@ -411,7 +424,7 @@ static int kirkwood_i2s_init(struct kirkwood_dma_data *priv)
writel(value, priv->io + KIRKWOOD_PLAYCTL);
value = readl(priv->io + KIRKWOOD_RECCTL);
value &= ~(KIRKWOOD_RECCTL_I2S_EN | KIRKWOOD_RECCTL_SPDIF_EN);
value &= ~KIRKWOOD_RECCTL_ENABLE_MASK;
writel(value, priv->io + KIRKWOOD_RECCTL);
return 0;
......
/*
* kirkwood-openrd.c
*
* (c) 2010 Arnaud Patard <apatard@mandriva.com>
* (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <sound/soc.h>
#include <linux/platform_data/asoc-kirkwood.h>
#include "../codecs/cs42l51.h"
static int openrd_client_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
unsigned int freq;
switch (params_rate(params)) {
default:
case 44100:
freq = 11289600;
break;
case 48000:
freq = 12288000;
break;
case 96000:
freq = 24576000;
break;
}
return snd_soc_dai_set_sysclk(codec_dai, 0, freq, SND_SOC_CLOCK_IN);
}
static struct snd_soc_ops openrd_client_ops = {
.hw_params = openrd_client_hw_params,
};
static struct snd_soc_dai_link openrd_client_dai[] = {
{
.name = "CS42L51",
.stream_name = "CS42L51 HiFi",
.cpu_dai_name = "i2s",
.platform_name = "mvebu-audio",
.codec_dai_name = "cs42l51-hifi",
.codec_name = "cs42l51-codec.0-004a",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
.ops = &openrd_client_ops,
},
};
static struct snd_soc_card openrd_client = {
.name = "OpenRD Client",
.owner = THIS_MODULE,
.dai_link = openrd_client_dai,
.num_links = ARRAY_SIZE(openrd_client_dai),
};
static int openrd_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &openrd_client;
int 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);
return ret;
}
static int openrd_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
snd_soc_unregister_card(card);
return 0;
}
static struct platform_driver openrd_driver = {
.driver = {
.name = "openrd-client-audio",
.owner = THIS_MODULE,
},
.probe = openrd_probe,
.remove = openrd_remove,
};
module_platform_driver(openrd_driver);
/* Module information */
MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
MODULE_DESCRIPTION("ALSA SoC OpenRD Client");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:openrd-client-audio");
/*
* kirkwood-t5325.c
*
* (c) 2010 Arnaud Patard <arnaud.patard@rtp-net.org>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <sound/soc.h>
#include <linux/platform_data/asoc-kirkwood.h>
#include "../codecs/alc5623.h"
static int t5325_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
unsigned int freq;
freq = params_rate(params) * 256;
return snd_soc_dai_set_sysclk(codec_dai, 0, freq, SND_SOC_CLOCK_IN);
}
static struct snd_soc_ops t5325_ops = {
.hw_params = t5325_hw_params,
};
static const struct snd_soc_dapm_widget t5325_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_SPK("Speaker", NULL),
SND_SOC_DAPM_MIC("Mic Jack", NULL),
};
static const struct snd_soc_dapm_route t5325_route[] = {
{ "Headphone Jack", NULL, "HPL" },
{ "Headphone Jack", NULL, "HPR" },
{"Speaker", NULL, "SPKOUT"},
{"Speaker", NULL, "SPKOUTN"},
{ "MIC1", NULL, "Mic Jack" },
{ "MIC2", NULL, "Mic Jack" },
};
static struct snd_soc_dai_link t5325_dai[] = {
{
.name = "ALC5621",
.stream_name = "ALC5621 HiFi",
.cpu_dai_name = "i2s",
.platform_name = "mvebu-audio",
.codec_dai_name = "alc5621-hifi",
.codec_name = "alc562x-codec.0-001a",
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
.ops = &t5325_ops,
},
};
static struct snd_soc_card t5325 = {
.name = "t5325",
.owner = THIS_MODULE,
.dai_link = t5325_dai,
.num_links = ARRAY_SIZE(t5325_dai),
.dapm_widgets = t5325_dapm_widgets,
.num_dapm_widgets = ARRAY_SIZE(t5325_dapm_widgets),
.dapm_routes = t5325_route,
.num_dapm_routes = ARRAY_SIZE(t5325_route),
};
static int t5325_probe(struct platform_device *pdev)
{
struct snd_soc_card *card = &t5325;
int 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);
return ret;
}
static int t5325_remove(struct platform_device *pdev)
{
struct snd_soc_card *card = platform_get_drvdata(pdev);
snd_soc_unregister_card(card);
return 0;
}
static struct platform_driver t5325_driver = {
.driver = {
.name = "t5325-audio",
.owner = THIS_MODULE,
},
.probe = t5325_probe,
.remove = t5325_remove,
};
module_platform_driver(t5325_driver);
MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
MODULE_DESCRIPTION("ALSA SoC t5325 audio client");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:t5325-audio");
......@@ -38,6 +38,9 @@
#define KIRKWOOD_RECCTL_SIZE_24 (1<<0)
#define KIRKWOOD_RECCTL_SIZE_32 (0<<0)
#define KIRKWOOD_RECCTL_ENABLE_MASK (KIRKWOOD_RECCTL_SPDIF_EN | \
KIRKWOOD_RECCTL_I2S_EN)
#define KIRKWOOD_REC_BUF_ADDR 0x1004
#define KIRKWOOD_REC_BUF_SIZE 0x1008
#define KIRKWOOD_REC_BYTE_COUNT 0x100C
......@@ -121,9 +124,9 @@
/* Theses values come from the marvell alsa driver */
/* need to find where they come from */
#define KIRKWOOD_SND_MIN_PERIODS 8
#define KIRKWOOD_SND_MIN_PERIODS 2
#define KIRKWOOD_SND_MAX_PERIODS 16
#define KIRKWOOD_SND_MIN_PERIOD_BYTES 0x800
#define KIRKWOOD_SND_MIN_PERIOD_BYTES 256
#define KIRKWOOD_SND_MAX_PERIOD_BYTES 0x8000
#define KIRKWOOD_SND_MAX_BUFFER_BYTES (KIRKWOOD_SND_MAX_PERIOD_BYTES \
* KIRKWOOD_SND_MAX_PERIODS)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment