Created
March 30, 2015 14:05
-
-
Save sbourdeauducq/8c5da8ac0da13e21ee31 to your computer and use it in GitHub Desktop.
thinkpad10 audio driver attempt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* byt_rt5670_bsw_rt5672.c - ASoc Machine driver for Intel Cherryview-based platforms | |
* Cherrytrail and Braswell, with RT5672 codec. | |
* | |
* Copyright (C) 2014 Intel Corp | |
* Author: Subhransu S. Prusty <[email protected]> | |
* Mengdong Lin <[email protected]> | |
* | |
* 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. | |
*/ | |
#include <linux/module.h> | |
#include <linux/platform_device.h> | |
#include <linux/slab.h> | |
#include <sound/pcm.h> | |
#include <sound/pcm_params.h> | |
#include <sound/soc.h> | |
#include "../codecs/rt5670.h" | |
#include "sst-atom-controls.h" | |
/* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */ | |
#define BYT_RT5670_PLAT_CLK_3_HZ 19200000 | |
#define BYT_RT5670_CODEC_DAI "rt5670-aif1" | |
static inline struct snd_soc_dai *byt_rt5670_get_codec_dai(struct snd_soc_card *card) | |
{ | |
int i; | |
for (i = 0; i < card->num_rtd; i++) { | |
struct snd_soc_pcm_runtime *rtd; | |
rtd = card->rtd + i; | |
if (!strncmp(rtd->codec_dai->name, BYT_RT5670_CODEC_DAI, | |
strlen(BYT_RT5670_CODEC_DAI))) | |
return rtd->codec_dai; | |
} | |
return NULL; | |
} | |
static int platform_clock_control(struct snd_soc_dapm_widget *w, | |
struct snd_kcontrol *k, int event) | |
{ | |
struct snd_soc_dapm_context *dapm = w->dapm; | |
struct snd_soc_card *card = dapm->card; | |
struct snd_soc_dai *codec_dai; | |
codec_dai = byt_rt5670_get_codec_dai(card); | |
if (!codec_dai) { | |
dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n"); | |
return -EIO; | |
} | |
if (!SND_SOC_DAPM_EVENT_OFF(event)) | |
return 0; | |
/* Set codec sysclk source to its internal clock because codec PLL will | |
* be off when idle and MCLK will also be off by ACPI when codec is | |
* runtime suspended. Codec needs clock for jack detection and button | |
* press. | |
*/ | |
snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK, | |
0, SND_SOC_CLOCK_IN); | |
return 0; | |
} | |
static const struct snd_soc_dapm_widget byt_rt5670_dapm_widgets[] = { | |
SND_SOC_DAPM_HP("Headphone", NULL), | |
SND_SOC_DAPM_MIC("Headset Mic", NULL), | |
SND_SOC_DAPM_MIC("Int Mic", NULL), | |
SND_SOC_DAPM_SPK("Ext Spk", NULL), | |
SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, | |
platform_clock_control, SND_SOC_DAPM_POST_PMD), | |
}; | |
static const struct snd_soc_dapm_route byt_rt5670_audio_map[] = { | |
{"IN1P", NULL, "Headset Mic"}, | |
{"IN1N", NULL, "Headset Mic"}, | |
{"DMIC L1", NULL, "Int Mic"}, | |
{"DMIC R1", NULL, "Int Mic"}, | |
{"Headphone", NULL, "HPOL"}, | |
{"Headphone", NULL, "HPOR"}, | |
{"Ext Spk", NULL, "SPOLP"}, | |
{"Ext Spk", NULL, "SPOLN"}, | |
{"Ext Spk", NULL, "SPORP"}, | |
{"Ext Spk", NULL, "SPORN"}, | |
{"AIF1 Playback", NULL, "ssp2 Tx"}, | |
{"ssp2 Tx", NULL, "codec_out0"}, | |
{"ssp2 Tx", NULL, "codec_out1"}, | |
{"codec_in0", NULL, "ssp2 Rx"}, | |
{"codec_in1", NULL, "ssp2 Rx"}, | |
{"ssp2 Rx", NULL, "AIF1 Capture"}, | |
{"Headphone", NULL, "Platform Clock"}, | |
{"Headset Mic", NULL, "Platform Clock"}, | |
{"Int Mic", NULL, "Platform Clock"}, | |
{"Ext Spk", NULL, "Platform Clock"}, | |
}; | |
static const struct snd_kcontrol_new byt_rt5670_mc_controls[] = { | |
SOC_DAPM_PIN_SWITCH("Headphone"), | |
SOC_DAPM_PIN_SWITCH("Headset Mic"), | |
SOC_DAPM_PIN_SWITCH("Int Mic"), | |
SOC_DAPM_PIN_SWITCH("Ext Spk"), | |
}; | |
static int byt_rt5670_aif1_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; | |
/* set codec PLL source to the 19.2MHz platform clock (MCLK) */ | |
ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK, | |
BYT_RT5670_PLAT_CLK_3_HZ, params_rate(params) * 512); | |
if (ret < 0) { | |
dev_err(rtd->dev, "can't set codec pll: %d\n", ret); | |
return ret; | |
} | |
/* set codec sysclk source to PLL */ | |
ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1, | |
params_rate(params) * 512, | |
SND_SOC_CLOCK_IN); | |
if (ret < 0) { | |
dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret); | |
return ret; | |
} | |
return 0; | |
} | |
static int byt_rt5670_codec_init(struct snd_soc_pcm_runtime *runtime) | |
{ | |
int ret; | |
struct snd_soc_dai *codec_dai = runtime->codec_dai; | |
struct snd_soc_codec *codec = codec_dai->codec; | |
/* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */ | |
ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24); | |
if (ret < 0) { | |
dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret); | |
return ret; | |
} | |
/* Select codec ASRC clock source to track I2S1 clock, because codec | |
* is in slave mode and 100fs I2S format (BCLK = 100 * LRCLK) cannot | |
* be supported by RT5672. Otherwise, ASRC will be disabled and cause | |
* noise. | |
*/ | |
rt5670_sel_asrc_clk_src(codec, | |
RT5670_DA_STEREO_FILTER | |
| RT5670_DA_MONO_L_FILTER | |
| RT5670_DA_MONO_R_FILTER | |
| RT5670_AD_STEREO_FILTER | |
| RT5670_AD_MONO_L_FILTER | |
| RT5670_AD_MONO_R_FILTER, | |
RT5670_CLK_SEL_I2S1_ASRC); | |
return 0; | |
} | |
static int byt_rt5670_codec_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 DSP will covert the FE rate to 48k, stereo, 24bits */ | |
rate->min = rate->max = 48000; | |
channels->min = channels->max = 2; | |
/* set SSP2 to 24-bit */ | |
snd_mask_set(¶ms->masks[SNDRV_PCM_HW_PARAM_FORMAT - | |
SNDRV_PCM_HW_PARAM_FIRST_MASK], | |
SNDRV_PCM_FORMAT_S24_LE); | |
return 0; | |
} | |
static unsigned int rates_48000[] = { | |
48000, | |
}; | |
static struct snd_pcm_hw_constraint_list constraints_48000 = { | |
.count = ARRAY_SIZE(rates_48000), | |
.list = rates_48000, | |
}; | |
static int byt_rt5670_aif1_startup(struct snd_pcm_substream *substream) | |
{ | |
return snd_pcm_hw_constraint_list(substream->runtime, 0, | |
SNDRV_PCM_HW_PARAM_RATE, | |
&constraints_48000); | |
} | |
static struct snd_soc_ops byt_rt5670_aif1_ops = { | |
.startup = byt_rt5670_aif1_startup, | |
}; | |
static struct snd_soc_ops byt_rt5670_be_ssp2_ops = { | |
.hw_params = byt_rt5670_aif1_hw_params, | |
}; | |
static struct snd_soc_dai_link byt_rt5670_dailink[] = { | |
{ | |
.name = "Baytrail Audio", | |
.stream_name = "Audio", | |
.cpu_dai_name = "baytrail-pcm-audio", | |
.codec_dai_name = "rt5670-aif1", | |
.codec_name = "i2c-10EC5640:00", | |
.platform_name = "baytrail-pcm-audio", | |
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | | |
SND_SOC_DAIFMT_CBS_CFS, | |
.init = byt_rt5670_codec_init, | |
.ops = &byt_rt5670_be_ssp2_ops, | |
}, | |
}; | |
/* SoC card */ | |
static struct snd_soc_card snd_soc_card_cht = { | |
.name = "thinkpadaudio", | |
.dai_link = byt_rt5670_dailink, | |
.num_links = ARRAY_SIZE(byt_rt5670_dailink), | |
.dapm_widgets = byt_rt5670_dapm_widgets, | |
.num_dapm_widgets = ARRAY_SIZE(byt_rt5670_dapm_widgets), | |
.dapm_routes = byt_rt5670_audio_map, | |
.num_dapm_routes = ARRAY_SIZE(byt_rt5670_audio_map), | |
.controls = byt_rt5670_mc_controls, | |
.num_controls = ARRAY_SIZE(byt_rt5670_mc_controls), | |
}; | |
static int snd_byt_rt5670_mc_probe(struct platform_device *pdev) | |
{ | |
int ret_val = 0; | |
/* register the soc card */ | |
snd_soc_card_cht.dev = &pdev->dev; | |
ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht); | |
if (ret_val) { | |
dev_err(&pdev->dev, | |
"snd_soc_register_card failed %d\n", ret_val); | |
return ret_val; | |
} | |
platform_set_drvdata(pdev, &snd_soc_card_cht); | |
return ret_val; | |
} | |
static struct platform_driver snd_byt_rt5670_mc_driver = { | |
.driver = { | |
.name = "byt-rt5670", | |
.pm = &snd_soc_pm_ops, | |
}, | |
.probe = snd_byt_rt5670_mc_probe, | |
}; | |
module_platform_driver(snd_byt_rt5670_mc_driver); | |
MODULE_DESCRIPTION("ASoC Intel(R) Baytrail Thinkpad Machine driver"); | |
MODULE_AUTHOR("Subhransu S. Prusty, Mengdong Lin, SB"); | |
MODULE_LICENSE("GPL v2"); | |
MODULE_ALIAS("platform:byt-rt5670"); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment