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. ...@@ -4,7 +4,7 @@ This device supports I2C only.
Required properties: Required properties:
- compatible : "maxim,max98090". - compatible : "maxim,max98090" or "maxim,max98091".
- reg : The I2C address of the device. - 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 ...@@ -75,6 +75,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_PCM3008 select SND_SOC_PCM3008
select SND_SOC_PCM512x_I2C if I2C select SND_SOC_PCM512x_I2C if I2C
select SND_SOC_PCM512x_SPI if SPI_MASTER select SND_SOC_PCM512x_SPI if SPI_MASTER
select SND_SOC_RT286 if I2C
select SND_SOC_RT5631 if I2C select SND_SOC_RT5631 if I2C
select SND_SOC_RT5640 if I2C select SND_SOC_RT5640 if I2C
select SND_SOC_RT5645 if I2C select SND_SOC_RT5645 if I2C
...@@ -455,6 +456,9 @@ config SND_SOC_RL6231 ...@@ -455,6 +456,9 @@ config SND_SOC_RL6231
default m if SND_SOC_RT5645=m default m if SND_SOC_RT5645=m
default m if SND_SOC_RT5651=m default m if SND_SOC_RT5651=m
config SND_SOC_RT286
tristate
config SND_SOC_RT5631 config SND_SOC_RT5631
tristate tristate
......
...@@ -69,6 +69,7 @@ snd-soc-pcm512x-objs := pcm512x.o ...@@ -69,6 +69,7 @@ snd-soc-pcm512x-objs := pcm512x.o
snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o
snd-soc-pcm512x-spi-objs := pcm512x-spi.o snd-soc-pcm512x-spi-objs := pcm512x-spi.o
snd-soc-rl6231-objs := rl6231.o snd-soc-rl6231-objs := rl6231.o
snd-soc-rt286-objs := rt286.o
snd-soc-rt5631-objs := rt5631.o snd-soc-rt5631-objs := rt5631.o
snd-soc-rt5640-objs := rt5640.o snd-soc-rt5640-objs := rt5640.o
snd-soc-rt5645-objs := rt5645.o snd-soc-rt5645-objs := rt5645.o
...@@ -237,6 +238,7 @@ obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.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_I2C) += snd-soc-pcm512x-i2c.o
obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.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_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_RT5631) += snd-soc-rt5631.o
obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o obj-$(CONFIG_SND_SOC_RT5640) += snd-soc-rt5640.o
obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o obj-$(CONFIG_SND_SOC_RT5645) += snd-soc-rt5645.o
......
...@@ -26,10 +26,6 @@ ...@@ -26,10 +26,6 @@
#include <sound/max98090.h> #include <sound/max98090.h>
#include "max98090.h" #include "max98090.h"
#define DEBUG
#define EXTMIC_METHOD
#define EXTMIC_METHOD_TEST
/* Allows for sparsely populated register maps */ /* Allows for sparsely populated register maps */
static struct reg_default max98090_reg[] = { static struct reg_default max98090_reg[] = {
{ 0x00, 0x00 }, /* 00 Software Reset */ { 0x00, 0x00 }, /* 00 Software Reset */
...@@ -820,7 +816,6 @@ static int max98090_micinput_event(struct snd_soc_dapm_widget *w, ...@@ -820,7 +816,6 @@ static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
else else
val = (val & M98090_MIC_PA2EN_MASK) >> M98090_MIC_PA2EN_SHIFT; val = (val & M98090_MIC_PA2EN_MASK) >> M98090_MIC_PA2EN_SHIFT;
if (val >= 1) { if (val >= 1) {
if (w->reg == M98090_REG_MIC1_INPUT_LEVEL) { if (w->reg == M98090_REG_MIC1_INPUT_LEVEL) {
max98090->pa1en = val - 1; /* Update for volatile */ max98090->pa1en = val - 1; /* Update for volatile */
...@@ -1140,7 +1135,6 @@ static const struct snd_kcontrol_new max98090_mixhprsel_mux = ...@@ -1140,7 +1135,6 @@ static const struct snd_kcontrol_new max98090_mixhprsel_mux =
SOC_DAPM_ENUM("MIXHPRSEL Mux", mixhprsel_mux_enum); SOC_DAPM_ENUM("MIXHPRSEL Mux", mixhprsel_mux_enum);
static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = { static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("MIC1"), SND_SOC_DAPM_INPUT("MIC1"),
SND_SOC_DAPM_INPUT("MIC2"), SND_SOC_DAPM_INPUT("MIC2"),
SND_SOC_DAPM_INPUT("DMICL"), SND_SOC_DAPM_INPUT("DMICL"),
...@@ -1304,7 +1298,6 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = { ...@@ -1304,7 +1298,6 @@ static const struct snd_soc_dapm_widget max98090_dapm_widgets[] = {
}; };
static const struct snd_soc_dapm_widget max98091_dapm_widgets[] = { static const struct snd_soc_dapm_widget max98091_dapm_widgets[] = {
SND_SOC_DAPM_INPUT("DMIC3"), SND_SOC_DAPM_INPUT("DMIC3"),
SND_SOC_DAPM_INPUT("DMIC4"), SND_SOC_DAPM_INPUT("DMIC4"),
...@@ -1315,7 +1308,6 @@ static const struct snd_soc_dapm_widget max98091_dapm_widgets[] = { ...@@ -1315,7 +1308,6 @@ static const struct snd_soc_dapm_widget max98091_dapm_widgets[] = {
}; };
static const struct snd_soc_dapm_route max98090_dapm_routes[] = { static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
{"MIC1 Input", NULL, "MIC1"}, {"MIC1 Input", NULL, "MIC1"},
{"MIC2 Input", NULL, "MIC2"}, {"MIC2 Input", NULL, "MIC2"},
...@@ -1493,17 +1485,14 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = { ...@@ -1493,17 +1485,14 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
{"SPKR", NULL, "SPK Right Out"}, {"SPKR", NULL, "SPK Right Out"},
{"RCVL", NULL, "RCV Left Out"}, {"RCVL", NULL, "RCV Left Out"},
{"RCVR", NULL, "RCV Right Out"}, {"RCVR", NULL, "RCV Right Out"},
}; };
static const struct snd_soc_dapm_route max98091_dapm_routes[] = { static const struct snd_soc_dapm_route max98091_dapm_routes[] = {
/* DMIC inputs */ /* DMIC inputs */
{"DMIC3", NULL, "DMIC3_ENA"}, {"DMIC3", NULL, "DMIC3_ENA"},
{"DMIC4", NULL, "DMIC4_ENA"}, {"DMIC4", NULL, "DMIC4_ENA"},
{"DMIC3", NULL, "AHPF"}, {"DMIC3", NULL, "AHPF"},
{"DMIC4", NULL, "AHPF"}, {"DMIC4", NULL, "AHPF"},
}; };
static int max98090_add_widgets(struct snd_soc_codec *codec) static int max98090_add_widgets(struct snd_soc_codec *codec)
...@@ -1531,7 +1520,6 @@ 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, snd_soc_dapm_add_routes(dapm, max98091_dapm_routes,
ARRAY_SIZE(max98091_dapm_routes)); ARRAY_SIZE(max98091_dapm_routes));
} }
return 0; return 0;
...@@ -2212,22 +2200,11 @@ static struct snd_soc_dai_driver max98090_dai[] = { ...@@ -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) static int max98090_probe(struct snd_soc_codec *codec)
{ {
struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec); struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
struct max98090_cdata *cdata; struct max98090_cdata *cdata;
enum max98090_type devtype;
int ret = 0; int ret = 0;
dev_dbg(codec->dev, "max98090_probe\n"); dev_dbg(codec->dev, "max98090_probe\n");
...@@ -2263,16 +2240,21 @@ static int max98090_probe(struct snd_soc_codec *codec) ...@@ -2263,16 +2240,21 @@ static int max98090_probe(struct snd_soc_codec *codec)
} }
if ((ret >= M98090_REVA) && (ret <= M98090_REVA + 0x0f)) { if ((ret >= M98090_REVA) && (ret <= M98090_REVA + 0x0f)) {
max98090->devtype = MAX98090; devtype = MAX98090;
dev_info(codec->dev, "MAX98090 REVID=0x%02x\n", ret); dev_info(codec->dev, "MAX98090 REVID=0x%02x\n", ret);
} else if ((ret >= M98091_REVA) && (ret <= M98091_REVA + 0x0f)) { } else if ((ret >= M98091_REVA) && (ret <= M98091_REVA + 0x0f)) {
max98090->devtype = MAX98091; devtype = MAX98091;
dev_info(codec->dev, "MAX98091 REVID=0x%02x\n", ret); dev_info(codec->dev, "MAX98091 REVID=0x%02x\n", ret);
} else { } else {
max98090->devtype = MAX98090; devtype = MAX98090;
dev_err(codec->dev, "Unrecognized revision 0x%02x\n", ret); 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; max98090->jack_state = M98090_JACK_STATE_NO_HEADSET;
INIT_DELAYED_WORK(&max98090->jack_work, max98090_jack_work); INIT_DELAYED_WORK(&max98090->jack_work, max98090_jack_work);
...@@ -2317,8 +2299,6 @@ static int max98090_probe(struct snd_soc_codec *codec) ...@@ -2317,8 +2299,6 @@ static int max98090_probe(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE, snd_soc_update_bits(codec, M98090_REG_MIC_BIAS_VOLTAGE,
M98090_MBVSEL_MASK, M98090_MBVSEL_2V8); M98090_MBVSEL_MASK, M98090_MBVSEL_2V8);
max98090_handle_pdata(codec);
max98090_add_widgets(codec); max98090_add_widgets(codec);
err_access: err_access:
...@@ -2428,7 +2408,7 @@ static int max98090_runtime_suspend(struct device *dev) ...@@ -2428,7 +2408,7 @@ static int max98090_runtime_suspend(struct device *dev)
} }
#endif #endif
#ifdef CONFIG_PM #ifdef CONFIG_PM_SLEEP
static int max98090_resume(struct device *dev) static int max98090_resume(struct device *dev)
{ {
struct max98090_priv *max98090 = dev_get_drvdata(dev); struct max98090_priv *max98090 = dev_get_drvdata(dev);
...@@ -2460,12 +2440,14 @@ static const struct dev_pm_ops max98090_pm = { ...@@ -2460,12 +2440,14 @@ static const struct dev_pm_ops max98090_pm = {
static const struct i2c_device_id max98090_i2c_id[] = { static const struct i2c_device_id max98090_i2c_id[] = {
{ "max98090", MAX98090 }, { "max98090", MAX98090 },
{ "max98091", MAX98091 },
{ } { }
}; };
MODULE_DEVICE_TABLE(i2c, max98090_i2c_id); MODULE_DEVICE_TABLE(i2c, max98090_i2c_id);
static const struct of_device_id max98090_of_match[] = { static const struct of_device_id max98090_of_match[] = {
{ .compatible = "maxim,max98090", }, { .compatible = "maxim,max98090", },
{ .compatible = "maxim,max98091", },
{ } { }
}; };
MODULE_DEVICE_TABLE(of, max98090_of_match); MODULE_DEVICE_TABLE(of, max98090_of_match);
......
...@@ -766,11 +766,11 @@ static int __init mc13783_codec_probe(struct platform_device *pdev) ...@@ -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); ret = of_property_read_u32(np, "adc-port", &priv->adc_ssi_port);
if (ret) if (ret)
return ret; goto out;
ret = of_property_read_u32(np, "dac-port", &priv->dac_ssi_port); ret = of_property_read_u32(np, "dac-port", &priv->dac_ssi_port);
if (ret) if (ret)
return ret; goto out;
} }
dev_set_drvdata(&pdev->dev, priv); dev_set_drvdata(&pdev->dev, priv);
...@@ -783,6 +783,8 @@ static int __init mc13783_codec_probe(struct platform_device *pdev) ...@@ -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, ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_mc13783,
mc13783_dai_async, ARRAY_SIZE(mc13783_dai_async)); mc13783_dai_async, ARRAY_SIZE(mc13783_dai_async));
out:
of_node_put(np);
return ret; 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 ...@@ -58,3 +58,15 @@ config SND_SOC_INTEL_BYT_MAX98090_MACH
help help
This adds audio driver for Intel Baytrail platform based boards This adds audio driver for Intel Baytrail platform based boards
with the MAX98090 audio codec. 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 ...@@ -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-haswell-objs := haswell.o
snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o
snd-soc-sst-byt-max98090-mach-objs := byt-max98090.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_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_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_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[] = { ...@@ -63,14 +63,6 @@ static struct snd_soc_jack_pin hs_jack_pins[] = {
.pin = "Headset Mic", .pin = "Headset Mic",
.mask = SND_JACK_MICROPHONE, .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[] = { static struct snd_soc_jack_gpio hs_jack_gpios[] = {
......
...@@ -34,6 +34,7 @@ static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = { ...@@ -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[] = { static const struct snd_soc_dapm_route byt_rt5640_audio_map[] = {
{"Headset Mic", NULL, "MICBIAS1"},
{"IN2P", NULL, "Headset Mic"}, {"IN2P", NULL, "Headset Mic"},
{"IN2N", NULL, "Headset Mic"}, {"IN2N", NULL, "Headset Mic"},
{"DMIC1", NULL, "Internal 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 { ...@@ -122,6 +122,26 @@ struct sst_byt_tstamp {
u32 channel_peak[8]; u32 channel_peak[8];
} __packed; } __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 */ /* driver internal IPC message structure */
struct ipc_message { struct ipc_message {
struct list_head list; struct list_head list;
...@@ -868,6 +888,7 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) ...@@ -868,6 +888,7 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
{ {
struct sst_byt *byt; struct sst_byt *byt;
struct sst_fw *byt_sst_fw; struct sst_fw *byt_sst_fw;
struct sst_byt_fw_init init;
int err; int err;
dev_dbg(dev, "initialising Byt DSP IPC\n"); dev_dbg(dev, "initialising Byt DSP IPC\n");
...@@ -929,6 +950,15 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata) ...@@ -929,6 +950,15 @@ int sst_byt_dsp_init(struct device *dev, struct sst_pdata *pdata)
goto boot_err; 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; pdata->dsp = byt;
byt->fw = byt_sst_fw; byt->fw = byt_sst_fw;
......
...@@ -224,19 +224,23 @@ EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64); ...@@ -224,19 +224,23 @@ EXPORT_SYMBOL_GPL(sst_dsp_shim_update_bits64);
void sst_dsp_dump(struct sst_dsp *sst) 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); EXPORT_SYMBOL_GPL(sst_dsp_dump);
void sst_dsp_reset(struct sst_dsp *sst) 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); EXPORT_SYMBOL_GPL(sst_dsp_reset);
int sst_dsp_boot(struct sst_dsp *sst) int sst_dsp_boot(struct sst_dsp *sst)
{ {
sst->ops->boot(sst); if (sst->ops->boot)
sst->ops->boot(sst);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(sst_dsp_boot); EXPORT_SYMBOL_GPL(sst_dsp_boot);
......
...@@ -52,7 +52,11 @@ ...@@ -52,7 +52,11 @@
#define SST_CLKCTL 0x78 #define SST_CLKCTL 0x78
#define SST_CSR2 0x80 #define SST_CSR2 0x80
#define SST_LTRC 0xE0 #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_DBGO 0xF0
#define SST_SHIM_SIZE 0x100 #define SST_SHIM_SIZE 0x100
...@@ -73,6 +77,8 @@ ...@@ -73,6 +77,8 @@
#define SST_CSR_S0IOCS (0x1 << 21) #define SST_CSR_S0IOCS (0x1 << 21)
#define SST_CSR_S1IOCS (0x1 << 23) #define SST_CSR_S1IOCS (0x1 << 23)
#define SST_CSR_LPCS (0x1 << 31) #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_RST (0x1 << 0)
#define SST_BYT_CSR_VECTOR_SEL (0x1 << 1) #define SST_BYT_CSR_VECTOR_SEL (0x1 << 1)
#define SST_BYT_CSR_STALL (0x1 << 2) #define SST_BYT_CSR_STALL (0x1 << 2)
...@@ -92,6 +98,14 @@ ...@@ -92,6 +98,14 @@
#define SST_IMRX_DONE (0x1 << 0) #define SST_IMRX_DONE (0x1 << 0)
#define SST_BYT_IMRX_REQUEST (0x1 << 1) #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 */ /* IPCX / IPCC */
#define SST_IPCX_DONE (0x1 << 30) #define SST_IPCX_DONE (0x1 << 30)
#define SST_IPCX_BUSY (0x1 << 31) #define SST_IPCX_BUSY (0x1 << 31)
...@@ -118,9 +132,21 @@ ...@@ -118,9 +132,21 @@
/* LTRC */ /* LTRC */
#define SST_LTRC_VAL(x) (x << 0) #define SST_LTRC_VAL(x) (x << 0)
/* HDMC */ /* HMDC */
#define SST_HDMC_HDDA0(x) (x << 0) #define SST_HMDC_HDDA0(x) (x << 0)
#define SST_HDMC_HDDA1(x) (x << 7) #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 */ /* SST Vendor Defined Registers and bits */
...@@ -130,11 +156,16 @@ ...@@ -130,11 +156,16 @@
#define SST_VDRTCTL3 0xaC #define SST_VDRTCTL3 0xaC
/* VDRTCTL0 */ /* VDRTCTL0 */
#define SST_VDRTCL0_APLLSE_MASK 1
#define SST_VDRTCL0_DSRAMPGE_SHIFT 16 #define SST_VDRTCL0_DSRAMPGE_SHIFT 16
#define SST_VDRTCL0_DSRAMPGE_MASK (0xffff << SST_VDRTCL0_DSRAMPGE_SHIFT) #define SST_VDRTCL0_DSRAMPGE_MASK (0xffff << SST_VDRTCL0_DSRAMPGE_SHIFT)
#define SST_VDRTCL0_ISRAMPGE_SHIFT 6 #define SST_VDRTCL0_ISRAMPGE_SHIFT 6
#define SST_VDRTCL0_ISRAMPGE_MASK (0x3ff << SST_VDRTCL0_ISRAMPGE_SHIFT) #define SST_VDRTCL0_ISRAMPGE_MASK (0x3ff << SST_VDRTCL0_ISRAMPGE_SHIFT)
/* PMCS */
#define SST_PMCS 0x84
#define SST_PMCS_PS_MASK 0x3
struct sst_dsp; struct sst_dsp;
/* /*
......
...@@ -28,9 +28,6 @@ ...@@ -28,9 +28,6 @@
#include <linux/firmware.h> #include <linux/firmware.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/acpi.h>
#include <acpi/acpi_bus.h>
#include "sst-dsp.h" #include "sst-dsp.h"
#include "sst-dsp-priv.h" #include "sst-dsp-priv.h"
#include "sst-haswell-ipc.h" #include "sst-haswell-ipc.h"
...@@ -272,9 +269,9 @@ static void hsw_boot(struct sst_dsp *sst) ...@@ -272,9 +269,9 @@ static void hsw_boot(struct sst_dsp *sst)
SST_CSR2_SDFD_SSP1); SST_CSR2_SDFD_SSP1);
/* enable DMA engine 0,1 all channels to access host memory */ /* enable DMA engine 0,1 all channels to access host memory */
sst_dsp_shim_update_bits_unlocked(sst, SST_HDMC, sst_dsp_shim_update_bits_unlocked(sst, SST_HMDC,
SST_HDMC_HDDA1(0xff) | SST_HDMC_HDDA0(0xff), SST_HMDC_HDDA1(0xff) | SST_HMDC_HDDA0(0xff),
SST_HDMC_HDDA1(0xff) | SST_HDMC_HDDA0(0xff)); SST_HMDC_HDDA1(0xff) | SST_HMDC_HDDA0(0xff));
/* disable all clock gating */ /* disable all clock gating */
writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL2); writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL2);
...@@ -313,9 +310,7 @@ static const struct sst_adsp_memregion lp_region[] = { ...@@ -313,9 +310,7 @@ static const struct sst_adsp_memregion lp_region[] = {
/* wild cat point ADSP mem regions */ /* wild cat point ADSP mem regions */
static const struct sst_adsp_memregion wpt_region[] = { static const struct sst_adsp_memregion wpt_region[] = {
{0x00000, 0x40000, 8, SST_MEM_DRAM}, /* D-SRAM0 - 8 * 32kB */ {0x00000, 0xA0000, 20, SST_MEM_DRAM}, /* D-SRAM0,D-SRAM1,D-SRAM2 - 20 * 32kB */
{0x40000, 0x80000, 8, SST_MEM_DRAM}, /* D-SRAM1 - 8 * 32kB */
{0x80000, 0xA0000, 4, SST_MEM_DRAM}, /* D-SRAM2 - 4 * 32kB */
{0xA0000, 0xF0000, 10, SST_MEM_IRAM}, /* I-SRAM - 10 * 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) ...@@ -339,21 +334,40 @@ static int hsw_acpi_resource_map(struct sst_dsp *sst, struct sst_pdata *pdata)
return 0; 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) 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) { for (index = 0; index < ARRAY_SIZE(sram_shift); index++) {
case SST_MEM_DRAM: if (sram_shift[index].dev_id == sst->id)
shift = 16; break;
break;
case SST_MEM_IRAM:
shift = 6;
break;
default:
return 0;
} }
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); bit = 1 << (block->index + shift);
return bit; return bit;
...@@ -501,8 +515,9 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata) ...@@ -501,8 +515,9 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
} }
} }
/* set default power gating mask */ /* set default power gating control, enable power gating control for all blocks. that is,
writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL0); can't be accessed, please enable each block before accessing. */
writel(0xffffffff, sst->addr.pci_cfg + SST_VDRTCTL0);
return 0; return 0;
} }
......
...@@ -183,7 +183,7 @@ struct sst_hsw_ipc_fw_ready { ...@@ -183,7 +183,7 @@ struct sst_hsw_ipc_fw_ready {
u32 inbox_size; u32 inbox_size;
u32 outbox_size; u32 outbox_size;
u32 fw_info_size; u32 fw_info_size;
u8 fw_info[1]; u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)];
} __attribute__((packed)); } __attribute__((packed));
struct ipc_message { struct ipc_message {
...@@ -457,9 +457,10 @@ static void ipc_tx_msgs(struct kthread_work *work) ...@@ -457,9 +457,10 @@ static void ipc_tx_msgs(struct kthread_work *work)
return; 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); 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); spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
return; return;
} }
...@@ -502,6 +503,7 @@ static int tx_wait_done(struct sst_hsw *hsw, struct ipc_message *msg, ...@@ -502,6 +503,7 @@ static int tx_wait_done(struct sst_hsw *hsw, struct ipc_message *msg,
ipc_shim_dbg(hsw, "message timeout"); ipc_shim_dbg(hsw, "message timeout");
trace_ipc_error("error message timeout for", msg->header); trace_ipc_error("error message timeout for", msg->header);
list_del(&msg->list);
ret = -ETIMEDOUT; ret = -ETIMEDOUT;
} else { } else {
...@@ -569,6 +571,9 @@ static void hsw_fw_ready(struct sst_hsw *hsw, u32 header) ...@@ -569,6 +571,9 @@ static void hsw_fw_ready(struct sst_hsw *hsw, u32 header)
{ {
struct sst_hsw_ipc_fw_ready fw_ready; struct sst_hsw_ipc_fw_ready fw_ready;
u32 offset; u32 offset;
u8 fw_info[IPC_MAX_MAILBOX_BYTES - 5 * sizeof(u32)];
char *tmp[5], *pinfo;
int i = 0;
offset = (header & 0x1FFFFFFF) << 3; offset = (header & 0x1FFFFFFF) << 3;
...@@ -589,6 +594,19 @@ static void hsw_fw_ready(struct sst_hsw *hsw, u32 header) ...@@ -589,6 +594,19 @@ static void hsw_fw_ready(struct sst_hsw *hsw, u32 header)
fw_ready.inbox_offset, fw_ready.inbox_size); fw_ready.inbox_offset, fw_ready.inbox_size);
dev_dbg(hsw->dev, " mailbox downstream 0x%x - size 0x%x\n", dev_dbg(hsw->dev, " mailbox downstream 0x%x - size 0x%x\n",
fw_ready.outbox_offset, fw_ready.outbox_size); 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) 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) ...@@ -671,7 +689,9 @@ static void hsw_stream_update(struct sst_hsw *hsw, struct ipc_message *msg)
switch (stream_msg) { switch (stream_msg) {
case IPC_STR_STAGE_MESSAGE: case IPC_STR_STAGE_MESSAGE:
case IPC_STR_NOTIFICATION: case IPC_STR_NOTIFICATION:
break;
case IPC_STR_RESET: case IPC_STR_RESET:
trace_ipc_notification("stream reset", stream->reply.stream_hw_id);
break; break;
case IPC_STR_PAUSE: case IPC_STR_PAUSE:
stream->running = false; stream->running = false;
...@@ -762,7 +782,8 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header) ...@@ -762,7 +782,8 @@ static int hsw_process_reply(struct sst_hsw *hsw, u32 header)
} }
/* update any stream states */ /* 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 ? */ /* wake up and return the error if we have waiters on this message ? */
list_del(&msg->list); list_del(&msg->list);
...@@ -1628,7 +1649,7 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw, ...@@ -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) enum sst_hsw_dx_state state, struct sst_hsw_ipc_dx_reply *dx)
{ {
u32 header, state_; u32 header, state_;
int ret; int ret, item;
header = IPC_GLB_TYPE(IPC_GLB_ENTER_DX_STATE); header = IPC_GLB_TYPE(IPC_GLB_ENTER_DX_STATE);
state_ = state; state_ = state;
...@@ -1642,6 +1663,13 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw, ...@@ -1642,6 +1663,13 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw,
return ret; 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", dev_dbg(hsw->dev, "ipc: got %d entry numbers for state %d\n",
dx->entries_no, state); dx->entries_no, state);
...@@ -1775,8 +1803,6 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata) ...@@ -1775,8 +1803,6 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
/* get the FW version */ /* get the FW version */
sst_hsw_fw_get_version(hsw, &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 */ /* get the globalmixer */
ret = sst_hsw_mixer_get_info(hsw); ret = sst_hsw_mixer_get_info(hsw);
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
/* /*
* sst_mfld_dsp.h - Intel SST Driver for audio engine * 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> * Authors: Vinod Koul <vinod.koul@linux.intel.com>
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* *
...@@ -19,6 +19,142 @@ ...@@ -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 { enum sst_codec_types {
/* AUDIO/MUSIC CODEC Type Definitions */ /* AUDIO/MUSIC CODEC Type Definitions */
SST_CODEC_TYPE_UNKNOWN = 0, SST_CODEC_TYPE_UNKNOWN = 0,
...@@ -35,14 +171,157 @@ enum stream_type { ...@@ -35,14 +171,157 @@ enum stream_type {
SST_STREAM_TYPE_MUSIC = 1, 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 { struct snd_pcm_params {
u8 num_chan; /* 1=Mono, 2=Stereo */ u8 num_chan; /* 1=Mono, 2=Stereo */
u8 pcm_wd_sz; /* 16/24 - bit*/ u8 pcm_wd_sz; /* 16/24 - bit*/
u32 reserved; /* Bitrate in bits per second */ u8 use_offload_path; /* 0-PCM using period elpased & ALSA interfaces
u32 sfreq; /* Sampling rate in Hz */ 1-PCM stream via compressed interface */
u8 use_offload_path;
u8 reserved2; u8 reserved2;
u16 reserved3; u32 sfreq; /* Sampling rate in Hz */
u8 channel_map[8]; u8 channel_map[8];
} __packed; } __packed;
...@@ -76,6 +355,7 @@ struct snd_aac_params { ...@@ -76,6 +355,7 @@ struct snd_aac_params {
struct snd_wma_params { struct snd_wma_params {
u8 num_chan; /* 1=Mono, 2=Stereo */ u8 num_chan; /* 1=Mono, 2=Stereo */
u8 pcm_wd_sz; /* 16/24 - bit*/ u8 pcm_wd_sz; /* 16/24 - bit*/
u16 reserved1;
u32 brate; /* Use the hard coded value. */ u32 brate; /* Use the hard coded value. */
u32 sfreq; /* Sampling freq eg. 8000, 441000, 48000 */ u32 sfreq; /* Sampling freq eg. 8000, 441000, 48000 */
u32 channel_mask; /* Channel Mask */ u32 channel_mask; /* Channel Mask */
...@@ -101,26 +381,153 @@ struct sst_address_info { ...@@ -101,26 +381,153 @@ struct sst_address_info {
}; };
struct snd_sst_alloc_params_ext { struct snd_sst_alloc_params_ext {
struct sst_address_info ring_buf_info[8]; __u16 sg_count;
u8 sg_count; __u16 reserved;
u8 reserved; __u32 frag_size; /*Number of samples after which period elapsed
u16 reserved2;
u32 frag_size; /*Number of samples after which period elapsed
message is sent valid only if path = 0*/ message is sent valid only if path = 0*/
} __packed; struct sst_address_info ring_buf_info[8];
};
struct snd_sst_stream_params { struct snd_sst_stream_params {
union snd_sst_codec_params uc; union snd_sst_codec_params uc;
} __packed; } __packed;
struct snd_sst_params { struct snd_sst_params {
u32 result;
u32 stream_id; u32 stream_id;
u8 codec; u8 codec;
u8 ops; u8 ops;
u8 stream_type; u8 stream_type;
u8 device_type; u8 device_type;
u8 task;
struct snd_sst_stream_params sparams; struct snd_sst_stream_params sparams;
struct snd_sst_alloc_params_ext aparams; 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__ */ #endif /* __SST_MFLD_DSP_H__ */
...@@ -100,14 +100,19 @@ static int sst_platform_compr_set_params(struct snd_compr_stream *cstream, ...@@ -100,14 +100,19 @@ static int sst_platform_compr_set_params(struct snd_compr_stream *cstream,
int retval; int retval;
struct snd_sst_params str_params; struct snd_sst_params str_params;
struct sst_compress_cb cb; 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; stream = cstream->runtime->private_data;
/* construct fw structure for this*/ /* construct fw structure for this*/
memset(&str_params, 0, sizeof(str_params)); memset(&str_params, 0, sizeof(str_params));
str_params.ops = STREAM_OPS_PLAYBACK; /* fill the device type and stream id to pass to SST driver */
str_params.stream_type = SST_STREAM_TYPE_MUSIC; retval = sst_fill_stream_params(cstream, ctx, &str_params, true);
str_params.device_type = SND_SST_DEVICE_COMPRESS; pr_debug("compr_set_params: fill stream params ret_val = 0x%x\n", retval);
if (retval < 0)
return retval;
switch (params->codec.id) { switch (params->codec.id) {
case SND_AUDIOCODEC_MP3: { case SND_AUDIOCODEC_MP3: {
......
/* /*
* sst_mfld_platform.c - Intel MID Platform driver * 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: Vinod Koul <vinod.koul@intel.com>
* Author: Harsha Priya <priya.harsha@intel.com> * Author: Harsha Priya <priya.harsha@intel.com>
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
...@@ -27,7 +27,9 @@ ...@@ -27,7 +27,9 @@
#include <sound/pcm_params.h> #include <sound/pcm_params.h>
#include <sound/soc.h> #include <sound/soc.h>
#include <sound/compress_driver.h> #include <sound/compress_driver.h>
#include <asm/platform_sst_audio.h>
#include "sst-mfld-platform.h" #include "sst-mfld-platform.h"
#include "sst-atom-controls.h"
struct sst_device *sst; struct sst_device *sst;
static DEFINE_MUTEX(sst_lock); static DEFINE_MUTEX(sst_lock);
...@@ -92,6 +94,13 @@ static struct snd_pcm_hardware sst_platform_pcm_hw = { ...@@ -92,6 +94,13 @@ static struct snd_pcm_hardware sst_platform_pcm_hw = {
.fifo_size = SST_FIFO_SIZE, .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 */ /* MFLD - MSIC */
static struct snd_soc_dai_driver sst_platform_dai[] = { 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) ...@@ -143,58 +152,142 @@ static inline int sst_get_stream_status(struct sst_runtime_stream *stream)
return state; 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, 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 = struct sst_runtime_stream *stream =
substream->runtime->private_data; substream->runtime->private_data;
struct sst_pcm_params param = {0}; struct snd_sst_stream_params param = {{{0,},},};
struct sst_stream_params str_params = {0}; struct snd_sst_params str_params = {0};
int ret_val; 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 */ /* set codec params and inform SST driver the same */
sst_fill_pcm_params(substream, &param); sst_fill_pcm_params(substream, &param);
sst_fill_alloc_params(substream, &alloc_params);
substream->runtime->dma_area = substream->dma_buffer.area; substream->runtime->dma_area = substream->dma_buffer.area;
str_params.sparams = param; str_params.sparams = param;
str_params.codec = param.codec; str_params.aparams = alloc_params;
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { str_params.codec = SST_CODEC_TYPE_PCM;
str_params.ops = STREAM_OPS_PLAYBACK;
str_params.device_type = substream->pcm->device + 1; /* fill the device type and stream id to pass to SST driver */
pr_debug("Playbck stream,Device %d\n", ret_val = sst_fill_stream_params(substream, ctx, &str_params, false);
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);
if (ret_val < 0) if (ret_val < 0)
return ret_val; return ret_val;
stream->stream_info.str_id = ret_val; stream->stream_info.str_id = str_params.stream_id;
pr_debug("str id : %d\n", stream->stream_info.str_id);
ret_val = stream->ops->open(&str_params);
if (ret_val <= 0)
return ret_val;
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; struct sst_runtime_stream *stream;
int status; int status;
...@@ -218,7 +311,7 @@ static int sst_platform_init_stream(struct snd_pcm_substream *substream) ...@@ -218,7 +311,7 @@ static int sst_platform_init_stream(struct snd_pcm_substream *substream)
pr_debug("setting buffer ptr param\n"); pr_debug("setting buffer ptr param\n");
sst_set_stream_status(stream, SST_PLATFORM_INIT); sst_set_stream_status(stream, SST_PLATFORM_INIT);
stream->stream_info.period_elapsed = sst_period_elapsed; 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.buffer_ptr = 0;
stream->stream_info.sfreq = substream->runtime->rate; stream->stream_info.sfreq = substream->runtime->rate;
ret_val = stream->ops->device_control( ret_val = stream->ops->device_control(
...@@ -230,19 +323,12 @@ static int sst_platform_init_stream(struct snd_pcm_substream *substream) ...@@ -230,19 +323,12 @@ static int sst_platform_init_stream(struct snd_pcm_substream *substream)
} }
/* end -- helper functions */ /* 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 snd_pcm_runtime *runtime = substream->runtime;
struct sst_runtime_stream *stream; 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); stream = kzalloc(sizeof(*stream), GFP_KERNEL);
if (!stream) if (!stream)
...@@ -251,50 +337,69 @@ static int sst_platform_open(struct snd_pcm_substream *substream) ...@@ -251,50 +337,69 @@ static int sst_platform_open(struct snd_pcm_substream *substream)
/* get the sst ops */ /* get the sst ops */
mutex_lock(&sst_lock); mutex_lock(&sst_lock);
if (!sst) { if (!sst ||
!try_module_get(sst->dev->driver->owner)) {
pr_err("no device available to run\n"); pr_err("no device available to run\n");
mutex_unlock(&sst_lock); ret_val = -ENODEV;
kfree(stream); goto out_ops;
return -ENODEV;
}
if (!try_module_get(sst->dev->driver->owner)) {
mutex_unlock(&sst_lock);
kfree(stream);
return -ENODEV;
} }
stream->ops = sst->ops; stream->ops = sst->ops;
mutex_unlock(&sst_lock); mutex_unlock(&sst_lock);
stream->stream_info.str_id = 0; 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 */ /* allocate memory for SST API set */
runtime->private_data = stream; 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; struct sst_runtime_stream *stream;
int ret_val = 0, str_id; int ret_val = 0, str_id;
pr_debug("sst_platform_close called\n");
stream = substream->runtime->private_data; stream = substream->runtime->private_data;
str_id = stream->stream_info.str_id; str_id = stream->stream_info.str_id;
if (str_id) if (str_id)
ret_val = stream->ops->close(str_id); ret_val = stream->ops->close(str_id);
module_put(sst->dev->driver->owner); module_put(sst->dev->driver->owner);
kfree(stream); 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; struct sst_runtime_stream *stream;
int ret_val = 0, str_id; int ret_val = 0, str_id;
pr_debug("sst_platform_pcm_prepare called\n");
stream = substream->runtime->private_data; stream = substream->runtime->private_data;
str_id = stream->stream_info.str_id; str_id = stream->stream_info.str_id;
if (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) ...@@ -303,8 +408,8 @@ static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream)
return ret_val; return ret_val;
} }
ret_val = sst_platform_alloc_stream(substream); ret_val = sst_platform_alloc_stream(substream, dai->platform);
if (ret_val < 0) if (ret_val <= 0)
return ret_val; return ret_val;
snprintf(substream->pcm->id, sizeof(substream->pcm->id), snprintf(substream->pcm->id, sizeof(substream->pcm->id),
"%d", stream->stream_info.str_id); "%d", stream->stream_info.str_id);
...@@ -316,6 +421,41 @@ static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream) ...@@ -316,6 +421,41 @@ static int sst_platform_pcm_prepare(struct snd_pcm_substream *substream)
return ret_val; 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, static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
int cmd) int cmd)
{ {
...@@ -331,7 +471,7 @@ static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream, ...@@ -331,7 +471,7 @@ static int sst_platform_pcm_trigger(struct snd_pcm_substream *substream,
pr_debug("sst: Trigger Start\n"); pr_debug("sst: Trigger Start\n");
str_cmd = SST_SND_START; str_cmd = SST_SND_START;
status = SST_PLATFORM_RUNNING; status = SST_PLATFORM_RUNNING;
stream->stream_info.mad_substream = substream; stream->stream_info.arg = substream;
break; break;
case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_STOP:
pr_debug("sst: in stop\n"); pr_debug("sst: in stop\n");
...@@ -377,32 +517,15 @@ static snd_pcm_uframes_t sst_platform_pcm_pointer ...@@ -377,32 +517,15 @@ static snd_pcm_uframes_t sst_platform_pcm_pointer
pr_err("sst: error code = %d\n", ret_val); pr_err("sst: error code = %d\n", ret_val);
return ret_val; return ret_val;
} }
return stream->stream_info.buffer_ptr; substream->runtime->delay = str_info->pcm_delay;
} return str_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);
} }
static struct snd_pcm_ops sst_platform_ops = { static struct snd_pcm_ops sst_platform_ops = {
.open = sst_platform_open, .open = sst_platform_open,
.close = sst_platform_close,
.ioctl = snd_pcm_lib_ioctl, .ioctl = snd_pcm_lib_ioctl,
.prepare = sst_platform_pcm_prepare,
.trigger = sst_platform_pcm_trigger, .trigger = sst_platform_pcm_trigger,
.pointer = sst_platform_pcm_pointer, .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) static void sst_pcm_free(struct snd_pcm *pcm)
...@@ -413,15 +536,15 @@ 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) 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; struct snd_pcm *pcm = rtd->pcm;
int retval = 0; int retval = 0;
pr_debug("sst_pcm_new called\n"); if (dai->driver->playback.channels_min ||
if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream || dai->driver->capture.channels_min) {
pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
retval = snd_pcm_lib_preallocate_pages_for_all(pcm, retval = snd_pcm_lib_preallocate_pages_for_all(pcm,
SNDRV_DMA_TYPE_CONTINUOUS, SNDRV_DMA_TYPE_CONTINUOUS,
snd_dma_continuous_data(GFP_KERNEL), snd_dma_continuous_data(GFP_DMA),
SST_MIN_BUFFER, SST_MAX_BUFFER); SST_MIN_BUFFER, SST_MAX_BUFFER);
if (retval) { if (retval) {
pr_err("dma buffer allocationf fail\n"); pr_err("dma buffer allocationf fail\n");
...@@ -445,10 +568,28 @@ static const struct snd_soc_component_driver sst_component = { ...@@ -445,10 +568,28 @@ static const struct snd_soc_component_driver sst_component = {
static int sst_platform_probe(struct platform_device *pdev) static int sst_platform_probe(struct platform_device *pdev)
{ {
struct sst_data *drv;
int ret; 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); ret = snd_soc_register_platform(&pdev->dev, &sst_soc_platform_drv);
if (ret) { if (ret) {
pr_err("registering soc platform failed\n"); pr_err("registering soc platform failed\n");
......
...@@ -39,9 +39,10 @@ extern struct sst_device *sst; ...@@ -39,9 +39,10 @@ extern struct sst_device *sst;
struct pcm_stream_info { struct pcm_stream_info {
int str_id; int str_id;
void *mad_substream; void *arg;
void (*period_elapsed) (void *mad_substream); void (*period_elapsed) (void *arg);
unsigned long long buffer_ptr; unsigned long long buffer_ptr;
unsigned long long pcm_delay;
int sfreq; int sfreq;
}; };
...@@ -62,7 +63,9 @@ enum sst_controls { ...@@ -62,7 +63,9 @@ enum sst_controls {
SST_SND_BUFFER_POINTER = 0x05, SST_SND_BUFFER_POINTER = 0x05,
SST_SND_STREAM_INIT = 0x06, SST_SND_STREAM_INIT = 0x06,
SST_SND_START = 0x07, 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 { enum sst_stream_ops {
...@@ -124,8 +127,9 @@ struct compress_sst_ops { ...@@ -124,8 +127,9 @@ struct compress_sst_ops {
}; };
struct 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 (*device_control) (int cmd, void *arg);
int (*set_generic_params)(enum sst_controls cmd, void *arg);
int (*close) (unsigned int str_id); int (*close) (unsigned int str_id);
}; };
...@@ -143,10 +147,27 @@ struct sst_device { ...@@ -143,10 +147,27 @@ struct sst_device {
char *name; char *name;
struct device *dev; struct device *dev;
struct sst_ops *ops; struct sst_ops *ops;
struct platform_device *pdev;
struct compress_sst_ops *compr_ops; struct compress_sst_ops *compr_ops;
}; };
struct sst_data;
void sst_set_stream_status(struct sst_runtime_stream *stream, int state); 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_register_dsp(struct sst_device *sst);
int sst_unregister_dsp(struct sst_device *sst); int sst_unregister_dsp(struct sst_device *sst);
#endif #endif
config SND_KIRKWOOD_SOC config SND_KIRKWOOD_SOC
tristate "SoC Audio for the Marvell Kirkwood and Dove chips" 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 help
Say Y or M if you want to add support for codecs attached to Say Y or M if you want to add support for codecs attached to
the Kirkwood I2S interface. You will also need to select the the Kirkwood I2S interface. You will also need to select the
...@@ -15,20 +15,3 @@ config SND_KIRKWOOD_SOC_ARMADA370_DB ...@@ -15,20 +15,3 @@ config SND_KIRKWOOD_SOC_ARMADA370_DB
Say Y if you want to add support for SoC audio on Say Y if you want to add support for SoC audio on
the Armada 370 Development Board. 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 ...@@ -2,10 +2,6 @@ snd-soc-kirkwood-objs := kirkwood-dma.o kirkwood-i2s.o
obj-$(CONFIG_SND_KIRKWOOD_SOC) += snd-soc-kirkwood.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 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_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) ...@@ -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 = { static struct snd_pcm_hardware kirkwood_dma_snd_hw = {
.info = (SNDRV_PCM_INFO_INTERLEAVED | .info = SNDRV_PCM_INFO_INTERLEAVED |
SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP |
SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_MMAP_VALID |
SNDRV_PCM_INFO_BLOCK_TRANSFER | SNDRV_PCM_INFO_BLOCK_TRANSFER |
SNDRV_PCM_INFO_PAUSE), SNDRV_PCM_INFO_PAUSE |
SNDRV_PCM_INFO_NO_PERIOD_WAKEUP,
.buffer_bytes_max = KIRKWOOD_SND_MAX_BUFFER_BYTES, .buffer_bytes_max = KIRKWOOD_SND_MAX_BUFFER_BYTES,
.period_bytes_min = KIRKWOOD_SND_MIN_PERIOD_BYTES, .period_bytes_min = KIRKWOOD_SND_MIN_PERIOD_BYTES,
.period_bytes_max = KIRKWOOD_SND_MAX_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, ...@@ -212,7 +212,8 @@ static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
KIRKWOOD_PLAYCTL_SIZE_MASK); KIRKWOOD_PLAYCTL_SIZE_MASK);
priv->ctl_play |= ctl_play; priv->ctl_play |= ctl_play;
} else { } else {
priv->ctl_rec &= ~KIRKWOOD_RECCTL_SIZE_MASK; priv->ctl_rec &= ~(KIRKWOOD_RECCTL_ENABLE_MASK |
KIRKWOOD_RECCTL_SIZE_MASK);
priv->ctl_rec |= ctl_rec; priv->ctl_rec |= ctl_rec;
} }
...@@ -221,14 +222,24 @@ static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream, ...@@ -221,14 +222,24 @@ static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
return 0; 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, static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
int cmd, struct snd_soc_dai *dai) 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); struct kirkwood_dma_data *priv = snd_soc_dai_get_drvdata(dai);
uint32_t ctl, value; uint32_t ctl, value;
ctl = readl(priv->io + KIRKWOOD_PLAYCTL); ctl = readl(priv->io + KIRKWOOD_PLAYCTL);
if (ctl & KIRKWOOD_PLAYCTL_PAUSE) { if ((ctl & KIRKWOOD_PLAYCTL_ENABLE_MASK) == 0) {
unsigned timeout = 5000; unsigned timeout = 5000;
/* /*
* The Armada510 spec says that if we enter pause mode, the * 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, ...@@ -256,14 +267,16 @@ static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
ctl &= ~KIRKWOOD_PLAYCTL_SPDIF_EN; /* i2s */ ctl &= ~KIRKWOOD_PLAYCTL_SPDIF_EN; /* i2s */
else else
ctl &= ~KIRKWOOD_PLAYCTL_I2S_EN; /* spdif */ ctl &= ~KIRKWOOD_PLAYCTL_I2S_EN; /* spdif */
ctl = kirkwood_i2s_play_mute(ctl);
value = ctl & ~KIRKWOOD_PLAYCTL_ENABLE_MASK; value = ctl & ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
writel(value, priv->io + KIRKWOOD_PLAYCTL); writel(value, priv->io + KIRKWOOD_PLAYCTL);
/* enable interrupts */ /* enable interrupts */
value = readl(priv->io + KIRKWOOD_INT_MASK); if (!runtime->no_period_wakeup) {
value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES; value = readl(priv->io + KIRKWOOD_INT_MASK);
writel(value, priv->io + KIRKWOOD_INT_MASK); value |= KIRKWOOD_INT_CAUSE_PLAY_BYTES;
writel(value, priv->io + KIRKWOOD_INT_MASK);
}
/* enable playback */ /* enable playback */
writel(ctl, priv->io + KIRKWOOD_PLAYCTL); writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
...@@ -295,6 +308,7 @@ static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream, ...@@ -295,6 +308,7 @@ static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE | ctl &= ~(KIRKWOOD_PLAYCTL_PAUSE | KIRKWOOD_PLAYCTL_I2S_MUTE |
KIRKWOOD_PLAYCTL_SPDIF_MUTE); KIRKWOOD_PLAYCTL_SPDIF_MUTE);
ctl = kirkwood_i2s_play_mute(ctl);
writel(ctl, priv->io + KIRKWOOD_PLAYCTL); writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
break; break;
...@@ -322,8 +336,7 @@ static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream, ...@@ -322,8 +336,7 @@ static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream,
else else
ctl &= ~KIRKWOOD_RECCTL_I2S_EN; /* spdif */ ctl &= ~KIRKWOOD_RECCTL_I2S_EN; /* spdif */
value = ctl & ~(KIRKWOOD_RECCTL_I2S_EN | value = ctl & ~KIRKWOOD_RECCTL_ENABLE_MASK;
KIRKWOOD_RECCTL_SPDIF_EN);
writel(value, priv->io + KIRKWOOD_RECCTL); writel(value, priv->io + KIRKWOOD_RECCTL);
/* enable interrupts */ /* enable interrupts */
...@@ -347,7 +360,7 @@ static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream, ...@@ -347,7 +360,7 @@ static int kirkwood_i2s_rec_trigger(struct snd_pcm_substream *substream,
/* disable all records */ /* disable all records */
value = readl(priv->io + KIRKWOOD_RECCTL); 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); writel(value, priv->io + KIRKWOOD_RECCTL);
break; break;
...@@ -411,7 +424,7 @@ static int kirkwood_i2s_init(struct kirkwood_dma_data *priv) ...@@ -411,7 +424,7 @@ static int kirkwood_i2s_init(struct kirkwood_dma_data *priv)
writel(value, priv->io + KIRKWOOD_PLAYCTL); writel(value, priv->io + KIRKWOOD_PLAYCTL);
value = readl(priv->io + KIRKWOOD_RECCTL); 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); writel(value, priv->io + KIRKWOOD_RECCTL);
return 0; 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 @@ ...@@ -38,6 +38,9 @@
#define KIRKWOOD_RECCTL_SIZE_24 (1<<0) #define KIRKWOOD_RECCTL_SIZE_24 (1<<0)
#define KIRKWOOD_RECCTL_SIZE_32 (0<<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_ADDR 0x1004
#define KIRKWOOD_REC_BUF_SIZE 0x1008 #define KIRKWOOD_REC_BUF_SIZE 0x1008
#define KIRKWOOD_REC_BYTE_COUNT 0x100C #define KIRKWOOD_REC_BYTE_COUNT 0x100C
...@@ -121,9 +124,9 @@ ...@@ -121,9 +124,9 @@
/* Theses values come from the marvell alsa driver */ /* Theses values come from the marvell alsa driver */
/* need to find where they come from */ /* 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_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_PERIOD_BYTES 0x8000
#define KIRKWOOD_SND_MAX_BUFFER_BYTES (KIRKWOOD_SND_MAX_PERIOD_BYTES \ #define KIRKWOOD_SND_MAX_BUFFER_BYTES (KIRKWOOD_SND_MAX_PERIOD_BYTES \
* KIRKWOOD_SND_MAX_PERIODS) * 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