smdk_wm8580.c (5036B)
1// SPDX-License-Identifier: GPL-2.0+ 2// 3// Copyright (c) 2009 Samsung Electronics Co. Ltd 4// Author: Jaswinder Singh <jassisinghbrar@gmail.com> 5 6#include <linux/module.h> 7#include <sound/soc.h> 8#include <sound/pcm_params.h> 9 10#include "../codecs/wm8580.h" 11#include "i2s.h" 12 13/* 14 * Default CFG switch settings to use this driver: 15 * 16 * SMDK6410: Set CFG1 1-3 Off, CFG2 1-4 On 17 */ 18 19/* SMDK has a 12MHZ crystal attached to WM8580 */ 20#define SMDK_WM8580_FREQ 12000000 21 22static int smdk_hw_params(struct snd_pcm_substream *substream, 23 struct snd_pcm_hw_params *params) 24{ 25 struct snd_soc_pcm_runtime *rtd = asoc_substream_to_rtd(substream); 26 struct snd_soc_dai *codec_dai = asoc_rtd_to_codec(rtd, 0); 27 unsigned int pll_out; 28 int rfs, ret; 29 30 switch (params_width(params)) { 31 case 8: 32 case 16: 33 break; 34 default: 35 return -EINVAL; 36 } 37 38 /* The Fvco for WM8580 PLLs must fall within [90,100]MHz. 39 * This criterion can't be met if we request PLL output 40 * as {8000x256, 64000x256, 11025x256}Hz. 41 * As a wayout, we rather change rfs to a minimum value that 42 * results in (params_rate(params) * rfs), and itself, acceptable 43 * to both - the CODEC and the CPU. 44 */ 45 switch (params_rate(params)) { 46 case 16000: 47 case 22050: 48 case 32000: 49 case 44100: 50 case 48000: 51 case 88200: 52 case 96000: 53 rfs = 256; 54 break; 55 case 64000: 56 rfs = 384; 57 break; 58 case 8000: 59 case 11025: 60 rfs = 512; 61 break; 62 default: 63 return -EINVAL; 64 } 65 pll_out = params_rate(params) * rfs; 66 67 /* Set WM8580 to drive MCLK from its PLLA */ 68 ret = snd_soc_dai_set_clkdiv(codec_dai, WM8580_MCLK, 69 WM8580_CLKSRC_PLLA); 70 if (ret < 0) 71 return ret; 72 73 ret = snd_soc_dai_set_pll(codec_dai, WM8580_PLLA, 0, 74 SMDK_WM8580_FREQ, pll_out); 75 if (ret < 0) 76 return ret; 77 78 ret = snd_soc_dai_set_sysclk(codec_dai, WM8580_CLKSRC_PLLA, 79 pll_out, SND_SOC_CLOCK_IN); 80 if (ret < 0) 81 return ret; 82 83 return 0; 84} 85 86/* 87 * SMDK WM8580 DAI operations. 88 */ 89static const struct snd_soc_ops smdk_ops = { 90 .hw_params = smdk_hw_params, 91}; 92 93/* SMDK Playback widgets */ 94static const struct snd_soc_dapm_widget smdk_wm8580_dapm_widgets[] = { 95 SND_SOC_DAPM_HP("Front", NULL), 96 SND_SOC_DAPM_HP("Center+Sub", NULL), 97 SND_SOC_DAPM_HP("Rear", NULL), 98 99 SND_SOC_DAPM_MIC("MicIn", NULL), 100 SND_SOC_DAPM_LINE("LineIn", NULL), 101}; 102 103/* SMDK-PAIFTX connections */ 104static const struct snd_soc_dapm_route smdk_wm8580_audio_map[] = { 105 /* MicIn feeds AINL */ 106 {"AINL", NULL, "MicIn"}, 107 108 /* LineIn feeds AINL/R */ 109 {"AINL", NULL, "LineIn"}, 110 {"AINR", NULL, "LineIn"}, 111 112 /* Front Left/Right are fed VOUT1L/R */ 113 {"Front", NULL, "VOUT1L"}, 114 {"Front", NULL, "VOUT1R"}, 115 116 /* Center/Sub are fed VOUT2L/R */ 117 {"Center+Sub", NULL, "VOUT2L"}, 118 {"Center+Sub", NULL, "VOUT2R"}, 119 120 /* Rear Left/Right are fed VOUT3L/R */ 121 {"Rear", NULL, "VOUT3L"}, 122 {"Rear", NULL, "VOUT3R"}, 123}; 124 125static int smdk_wm8580_init_paiftx(struct snd_soc_pcm_runtime *rtd) 126{ 127 /* Enabling the microphone requires the fitting of a 0R 128 * resistor to connect the line from the microphone jack. 129 */ 130 snd_soc_dapm_disable_pin(&rtd->card->dapm, "MicIn"); 131 132 return 0; 133} 134 135enum { 136 PRI_PLAYBACK = 0, 137 PRI_CAPTURE, 138}; 139 140#define SMDK_DAI_FMT (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | \ 141 SND_SOC_DAIFMT_CBM_CFM) 142 143SND_SOC_DAILINK_DEFS(paif_rx, 144 DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.2")), 145 DAILINK_COMP_ARRAY(COMP_CODEC("wm8580.0-001b", "wm8580-hifi-playback")), 146 DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0"))); 147 148SND_SOC_DAILINK_DEFS(paif_tx, 149 DAILINK_COMP_ARRAY(COMP_CPU("samsung-i2s.2")), 150 DAILINK_COMP_ARRAY(COMP_CODEC("wm8580.0-001b", "wm8580-hifi-capture")), 151 DAILINK_COMP_ARRAY(COMP_PLATFORM("samsung-i2s.0"))); 152 153static struct snd_soc_dai_link smdk_dai[] = { 154 [PRI_PLAYBACK] = { /* Primary Playback i/f */ 155 .name = "WM8580 PAIF RX", 156 .stream_name = "Playback", 157 .dai_fmt = SMDK_DAI_FMT, 158 .ops = &smdk_ops, 159 SND_SOC_DAILINK_REG(paif_rx), 160 }, 161 [PRI_CAPTURE] = { /* Primary Capture i/f */ 162 .name = "WM8580 PAIF TX", 163 .stream_name = "Capture", 164 .dai_fmt = SMDK_DAI_FMT, 165 .init = smdk_wm8580_init_paiftx, 166 .ops = &smdk_ops, 167 SND_SOC_DAILINK_REG(paif_tx), 168 }, 169}; 170 171static struct snd_soc_card smdk = { 172 .name = "SMDK-I2S", 173 .owner = THIS_MODULE, 174 .dai_link = smdk_dai, 175 .num_links = ARRAY_SIZE(smdk_dai), 176 177 .dapm_widgets = smdk_wm8580_dapm_widgets, 178 .num_dapm_widgets = ARRAY_SIZE(smdk_wm8580_dapm_widgets), 179 .dapm_routes = smdk_wm8580_audio_map, 180 .num_dapm_routes = ARRAY_SIZE(smdk_wm8580_audio_map), 181}; 182 183static struct platform_device *smdk_snd_device; 184 185static int __init smdk_audio_init(void) 186{ 187 int ret; 188 189 smdk_snd_device = platform_device_alloc("soc-audio", -1); 190 if (!smdk_snd_device) 191 return -ENOMEM; 192 193 platform_set_drvdata(smdk_snd_device, &smdk); 194 ret = platform_device_add(smdk_snd_device); 195 196 if (ret) 197 platform_device_put(smdk_snd_device); 198 199 return ret; 200} 201module_init(smdk_audio_init); 202 203static void __exit smdk_audio_exit(void) 204{ 205 platform_device_unregister(smdk_snd_device); 206} 207module_exit(smdk_audio_exit); 208 209MODULE_AUTHOR("Jaswinder Singh, jassisinghbrar@gmail.com"); 210MODULE_DESCRIPTION("ALSA SoC SMDK WM8580"); 211MODULE_LICENSE("GPL");