cregit-Linux how code gets into the kernel

Release 4.7 sound/soc/codecs/wm8960.c

Directory: sound/soc/codecs
/*
 * wm8960.c  --  WM8960 ALSA SoC Audio driver
 *
 * Copyright 2007-11 Wolfson Microelectronics, plc
 *
 * Author: Liam Girdwood
 *
 * 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/clk.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <sound/core.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/initval.h>
#include <sound/tlv.h>
#include <sound/wm8960.h>

#include "wm8960.h"

/* R25 - Power 1 */

#define WM8960_VMID_MASK 0x180

#define WM8960_VREF      0x40

/* R26 - Power 2 */

#define WM8960_PWR2_LOUT1	0x40

#define WM8960_PWR2_ROUT1	0x20

#define WM8960_PWR2_OUT3	0x02

/* R28 - Anti-pop 1 */

#define WM8960_POBCTRL   0x80

#define WM8960_BUFDCOPEN 0x10

#define WM8960_BUFIOEN   0x08

#define WM8960_SOFT_ST   0x04

#define WM8960_HPSTBY    0x01

/* R29 - Anti-pop 2 */

#define WM8960_DISOP     0x40

#define WM8960_DRES_MASK 0x30

static bool is_pll_freq_available(unsigned int source, unsigned int target);
static int wm8960_set_pll(struct snd_soc_codec *codec,
		unsigned int freq_in, unsigned int freq_out);
/*
 * wm8960 register cache
 * We can't read the WM8960 register space when we are
 * using 2 wire for device control, so we cache them instead.
 */

static const struct reg_default wm8960_reg_defaults[] = {
	{  0x0, 0x00a7 },
	{  0x1, 0x00a7 },
	{  0x2, 0x0000 },
	{  0x3, 0x0000 },
	{  0x4, 0x0000 },
	{  0x5, 0x0008 },
	{  0x6, 0x0000 },
	{  0x7, 0x000a },
	{  0x8, 0x01c0 },
	{  0x9, 0x0000 },
	{  0xa, 0x00ff },
	{  0xb, 0x00ff },

	{ 0x10, 0x0000 },
	{ 0x11, 0x007b },
	{ 0x12, 0x0100 },
	{ 0x13, 0x0032 },
	{ 0x14, 0x0000 },
	{ 0x15, 0x00c3 },
	{ 0x16, 0x00c3 },
	{ 0x17, 0x01c0 },
	{ 0x18, 0x0000 },
	{ 0x19, 0x0000 },
	{ 0x1a, 0x0000 },
	{ 0x1b, 0x0000 },
	{ 0x1c, 0x0000 },
	{ 0x1d, 0x0000 },

	{ 0x20, 0x0100 },
	{ 0x21, 0x0100 },
	{ 0x22, 0x0050 },

	{ 0x25, 0x0050 },
	{ 0x26, 0x0000 },
	{ 0x27, 0x0000 },
	{ 0x28, 0x0000 },
	{ 0x29, 0x0000 },
	{ 0x2a, 0x0040 },
	{ 0x2b, 0x0000 },
	{ 0x2c, 0x0000 },
	{ 0x2d, 0x0050 },
	{ 0x2e, 0x0050 },
	{ 0x2f, 0x0000 },
	{ 0x30, 0x0002 },
	{ 0x31, 0x0037 },

	{ 0x33, 0x0080 },
	{ 0x34, 0x0008 },
	{ 0x35, 0x0031 },
	{ 0x36, 0x0026 },
	{ 0x37, 0x00e9 },
};


static bool wm8960_volatile(struct device *dev, unsigned int reg) { switch (reg) { case WM8960_RESET: return true; default: return false; } }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown31100.00%1100.00%
Total31100.00%1100.00%

struct wm8960_priv { struct clk *mclk; struct regmap *regmap; int (*set_bias_level)(struct snd_soc_codec *, enum snd_soc_bias_level level); struct snd_soc_dapm_widget *lout1; struct snd_soc_dapm_widget *rout1; struct snd_soc_dapm_widget *out3; bool deemph; int lrclk; int bclk; int sysclk; int clk_id; int freq_in; bool is_stream_in_use[2]; struct wm8960_data pdata; }; #define wm8960_reset(c) regmap_write(c, WM8960_RESET, 0) /* enumerated controls */ static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted", "Right Inverted", "Stereo Inversion"}; static const char *wm8960_3d_upper_cutoff[] = {"High", "Low"}; static const char *wm8960_3d_lower_cutoff[] = {"Low", "High"}; static const char *wm8960_alcfunc[] = {"Off", "Right", "Left", "Stereo"}; static const char *wm8960_alcmode[] = {"ALC", "Limiter"}; static const char *wm8960_adc_data_output_sel[] = { "Left Data = Left ADC; Right Data = Right ADC", "Left Data = Left ADC; Right Data = Left ADC", "Left Data = Right ADC; Right Data = Right ADC", "Left Data = Right ADC; Right Data = Left ADC", }; static const char *wm8960_dmonomix[] = {"Stereo", "Mono"}; static const struct soc_enum wm8960_enum[] = { SOC_ENUM_SINGLE(WM8960_DACCTL1, 5, 4, wm8960_polarity), SOC_ENUM_SINGLE(WM8960_DACCTL2, 5, 4, wm8960_polarity), SOC_ENUM_SINGLE(WM8960_3D, 6, 2, wm8960_3d_upper_cutoff), SOC_ENUM_SINGLE(WM8960_3D, 5, 2, wm8960_3d_lower_cutoff), SOC_ENUM_SINGLE(WM8960_ALC1, 7, 4, wm8960_alcfunc), SOC_ENUM_SINGLE(WM8960_ALC3, 8, 2, wm8960_alcmode), SOC_ENUM_SINGLE(WM8960_ADDCTL1, 2, 4, wm8960_adc_data_output_sel), SOC_ENUM_SINGLE(WM8960_ADDCTL1, 4, 2, wm8960_dmonomix), }; static const int deemph_settings[] = { 0, 32000, 44100, 48000 };
static int wm8960_set_deemph(struct snd_soc_codec *codec) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); int val, i, best; /* If we're using deemphasis select the nearest available sample * rate. */ if (wm8960->deemph) { best = 1; for (i = 2; i < ARRAY_SIZE(deemph_settings); i++) { if (abs(deemph_settings[i] - wm8960->lrclk) < abs(deemph_settings[best] - wm8960->lrclk)) best = i; } val = best << 1; } else { val = 0; } dev_dbg(codec->dev, "Set deemphasis %d\n", val); return snd_soc_update_bits(codec, WM8960_DACCTL1, 0x6, val); }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown12398.40%150.00%
zidan wangzidan wang21.60%150.00%
Total125100.00%2100.00%


static int wm8960_get_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); ucontrol->value.integer.value[0] = wm8960->deemph; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown3972.22%125.00%
dmitry artamonowdmitry artamonow1222.22%125.00%
takashi iwaitakashi iwai23.70%125.00%
lars-peter clausenlars-peter clausen11.85%125.00%
Total54100.00%4100.00%


static int wm8960_put_deemph(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol); struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); unsigned int deemph = ucontrol->value.integer.value[0]; if (deemph > 1) return -EINVAL; wm8960->deemph = deemph; return wm8960_set_deemph(codec); }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown6994.52%125.00%
takashi iwaitakashi iwai22.74%125.00%
dan carpenterdan carpenter11.37%125.00%
lars-peter clausenlars-peter clausen11.37%125.00%
Total73100.00%4100.00%

static const DECLARE_TLV_DB_SCALE(adc_tlv, -9750, 50, 1); static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1725, 75, 0); static const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); static const DECLARE_TLV_DB_SCALE(bypass_tlv, -2100, 300, 0); static const DECLARE_TLV_DB_SCALE(out_tlv, -12100, 100, 1); static const DECLARE_TLV_DB_SCALE(lineinboost_tlv, -1500, 300, 1); static const unsigned int micboost_tlv[] = { TLV_DB_RANGE_HEAD(2), 0, 1, TLV_DB_SCALE_ITEM(0, 1300, 0), 2, 3, TLV_DB_SCALE_ITEM(2000, 900, 0), }; static const struct snd_kcontrol_new wm8960_snd_controls[] = { SOC_DOUBLE_R_TLV("Capture Volume", WM8960_LINVOL, WM8960_RINVOL, 0, 63, 0, inpga_tlv), SOC_DOUBLE_R("Capture Volume ZC Switch", WM8960_LINVOL, WM8960_RINVOL, 6, 1, 0), SOC_DOUBLE_R("Capture Switch", WM8960_LINVOL, WM8960_RINVOL, 7, 1, 1), SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT3 Volume", WM8960_INBMIX1, 4, 7, 0, lineinboost_tlv), SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT2 Volume", WM8960_INBMIX1, 1, 7, 0, lineinboost_tlv), SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT3 Volume", WM8960_INBMIX2, 4, 7, 0, lineinboost_tlv), SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT2 Volume", WM8960_INBMIX2, 1, 7, 0, lineinboost_tlv), SOC_SINGLE_TLV("Right Input Boost Mixer RINPUT1 Volume", WM8960_RINPATH, 4, 3, 0, micboost_tlv), SOC_SINGLE_TLV("Left Input Boost Mixer LINPUT1 Volume", WM8960_LINPATH, 4, 3, 0, micboost_tlv), SOC_DOUBLE_R_TLV("Playback Volume", WM8960_LDAC, WM8960_RDAC, 0, 255, 0, dac_tlv), SOC_DOUBLE_R_TLV("Headphone Playback Volume", WM8960_LOUT1, WM8960_ROUT1, 0, 127, 0, out_tlv), SOC_DOUBLE_R("Headphone Playback ZC Switch", WM8960_LOUT1, WM8960_ROUT1, 7, 1, 0), SOC_DOUBLE_R_TLV("Speaker Playback Volume", WM8960_LOUT2, WM8960_ROUT2, 0, 127, 0, out_tlv), SOC_DOUBLE_R("Speaker Playback ZC Switch", WM8960_LOUT2, WM8960_ROUT2, 7, 1, 0), SOC_SINGLE("Speaker DC Volume", WM8960_CLASSD3, 3, 5, 0), SOC_SINGLE("Speaker AC Volume", WM8960_CLASSD3, 0, 5, 0), SOC_SINGLE("PCM Playback -6dB Switch", WM8960_DACCTL1, 7, 1, 0), SOC_ENUM("ADC Polarity", wm8960_enum[0]), SOC_SINGLE("ADC High Pass Filter Switch", WM8960_DACCTL1, 0, 1, 0), SOC_ENUM("DAC Polarity", wm8960_enum[1]), SOC_SINGLE_BOOL_EXT("DAC Deemphasis Switch", 0, wm8960_get_deemph, wm8960_put_deemph), SOC_ENUM("3D Filter Upper Cut-Off", wm8960_enum[2]), SOC_ENUM("3D Filter Lower Cut-Off", wm8960_enum[3]), SOC_SINGLE("3D Volume", WM8960_3D, 1, 15, 0), SOC_SINGLE("3D Switch", WM8960_3D, 0, 1, 0), SOC_ENUM("ALC Function", wm8960_enum[4]), SOC_SINGLE("ALC Max Gain", WM8960_ALC1, 4, 7, 0), SOC_SINGLE("ALC Target", WM8960_ALC1, 0, 15, 1), SOC_SINGLE("ALC Min Gain", WM8960_ALC2, 4, 7, 0), SOC_SINGLE("ALC Hold Time", WM8960_ALC2, 0, 15, 0), SOC_ENUM("ALC Mode", wm8960_enum[5]), SOC_SINGLE("ALC Decay", WM8960_ALC3, 4, 15, 0), SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0), SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0), SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0), SOC_DOUBLE_R_TLV("ADC PCM Capture Volume", WM8960_LADC, WM8960_RADC, 0, 255, 0, adc_tlv), SOC_SINGLE_TLV("Left Output Mixer Boost Bypass Volume", WM8960_BYPASS1, 4, 7, 1, bypass_tlv), SOC_SINGLE_TLV("Left Output Mixer LINPUT3 Volume", WM8960_LOUTMIX, 4, 7, 1, bypass_tlv), SOC_SINGLE_TLV("Right Output Mixer Boost Bypass Volume", WM8960_BYPASS2, 4, 7, 1, bypass_tlv), SOC_SINGLE_TLV("Right Output Mixer RINPUT3 Volume", WM8960_ROUTMIX, 4, 7, 1, bypass_tlv), SOC_ENUM("ADC Data Output Select", wm8960_enum[6]), SOC_ENUM("DAC Mono Mix", wm8960_enum[7]), }; static const struct snd_kcontrol_new wm8960_lin_boost[] = { SOC_DAPM_SINGLE("LINPUT2 Switch", WM8960_LINPATH, 6, 1, 0), SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LINPATH, 7, 1, 0), SOC_DAPM_SINGLE("LINPUT1 Switch", WM8960_LINPATH, 8, 1, 0), }; static const struct snd_kcontrol_new wm8960_lin[] = { SOC_DAPM_SINGLE("Boost Switch", WM8960_LINPATH, 3, 1, 0), }; static const struct snd_kcontrol_new wm8960_rin_boost[] = { SOC_DAPM_SINGLE("RINPUT2 Switch", WM8960_RINPATH, 6, 1, 0), SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_RINPATH, 7, 1, 0), SOC_DAPM_SINGLE("RINPUT1 Switch", WM8960_RINPATH, 8, 1, 0), }; static const struct snd_kcontrol_new wm8960_rin[] = { SOC_DAPM_SINGLE("Boost Switch", WM8960_RINPATH, 3, 1, 0), }; static const struct snd_kcontrol_new wm8960_loutput_mixer[] = { SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_LOUTMIX, 8, 1, 0), SOC_DAPM_SINGLE("LINPUT3 Switch", WM8960_LOUTMIX, 7, 1, 0), SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS1, 7, 1, 0), }; static const struct snd_kcontrol_new wm8960_routput_mixer[] = { SOC_DAPM_SINGLE("PCM Playback Switch", WM8960_ROUTMIX, 8, 1, 0), SOC_DAPM_SINGLE("RINPUT3 Switch", WM8960_ROUTMIX, 7, 1, 0), SOC_DAPM_SINGLE("Boost Bypass Switch", WM8960_BYPASS2, 7, 1, 0), }; static const struct snd_kcontrol_new wm8960_mono_out[] = { SOC_DAPM_SINGLE("Left Switch", WM8960_MONOMIX1, 7, 1, 0), SOC_DAPM_SINGLE("Right Switch", WM8960_MONOMIX2, 7, 1, 0), }; static const struct snd_soc_dapm_widget wm8960_dapm_widgets[] = { SND_SOC_DAPM_INPUT("LINPUT1"), SND_SOC_DAPM_INPUT("RINPUT1"), SND_SOC_DAPM_INPUT("LINPUT2"), SND_SOC_DAPM_INPUT("RINPUT2"), SND_SOC_DAPM_INPUT("LINPUT3"), SND_SOC_DAPM_INPUT("RINPUT3"), SND_SOC_DAPM_SUPPLY("MICB", WM8960_POWER1, 1, 0, NULL, 0), SND_SOC_DAPM_MIXER("Left Boost Mixer", WM8960_POWER1, 5, 0, wm8960_lin_boost, ARRAY_SIZE(wm8960_lin_boost)), SND_SOC_DAPM_MIXER("Right Boost Mixer", WM8960_POWER1, 4, 0, wm8960_rin_boost, ARRAY_SIZE(wm8960_rin_boost)), SND_SOC_DAPM_MIXER("Left Input Mixer", WM8960_POWER3, 5, 0, wm8960_lin, ARRAY_SIZE(wm8960_lin)), SND_SOC_DAPM_MIXER("Right Input Mixer", WM8960_POWER3, 4, 0, wm8960_rin, ARRAY_SIZE(wm8960_rin)), SND_SOC_DAPM_ADC("Left ADC", "Capture", WM8960_POWER1, 3, 0), SND_SOC_DAPM_ADC("Right ADC", "Capture", WM8960_POWER1, 2, 0), SND_SOC_DAPM_DAC("Left DAC", "Playback", WM8960_POWER2, 8, 0), SND_SOC_DAPM_DAC("Right DAC", "Playback", WM8960_POWER2, 7, 0), SND_SOC_DAPM_MIXER("Left Output Mixer", WM8960_POWER3, 3, 0, &wm8960_loutput_mixer[0], ARRAY_SIZE(wm8960_loutput_mixer)), SND_SOC_DAPM_MIXER("Right Output Mixer", WM8960_POWER3, 2, 0, &wm8960_routput_mixer[0], ARRAY_SIZE(wm8960_routput_mixer)), SND_SOC_DAPM_PGA("LOUT1 PGA", WM8960_POWER2, 6, 0, NULL, 0), SND_SOC_DAPM_PGA("ROUT1 PGA", WM8960_POWER2, 5, 0, NULL, 0), SND_SOC_DAPM_PGA("Left Speaker PGA", WM8960_POWER2, 4, 0, NULL, 0), SND_SOC_DAPM_PGA("Right Speaker PGA", WM8960_POWER2, 3, 0, NULL, 0), SND_SOC_DAPM_PGA("Right Speaker Output", WM8960_CLASSD1, 7, 0, NULL, 0), SND_SOC_DAPM_PGA("Left Speaker Output", WM8960_CLASSD1, 6, 0, NULL, 0), SND_SOC_DAPM_OUTPUT("SPK_LP"), SND_SOC_DAPM_OUTPUT("SPK_LN"), SND_SOC_DAPM_OUTPUT("HP_L"), SND_SOC_DAPM_OUTPUT("HP_R"), SND_SOC_DAPM_OUTPUT("SPK_RP"), SND_SOC_DAPM_OUTPUT("SPK_RN"), SND_SOC_DAPM_OUTPUT("OUT3"), }; static const struct snd_soc_dapm_widget wm8960_dapm_widgets_out3[] = { SND_SOC_DAPM_MIXER("Mono Output Mixer", WM8960_POWER2, 1, 0, &wm8960_mono_out[0], ARRAY_SIZE(wm8960_mono_out)), }; /* Represent OUT3 as a PGA so that it gets turned on with LOUT1/ROUT1 */ static const struct snd_soc_dapm_widget wm8960_dapm_widgets_capless[] = { SND_SOC_DAPM_PGA("OUT3 VMID", WM8960_POWER2, 1, 0, NULL, 0), }; static const struct snd_soc_dapm_route audio_paths[] = { { "Left Boost Mixer", "LINPUT1 Switch", "LINPUT1" }, { "Left Boost Mixer", "LINPUT2 Switch", "LINPUT2" }, { "Left Boost Mixer", "LINPUT3 Switch", "LINPUT3" }, { "Left Input Mixer", "Boost Switch", "Left Boost Mixer" }, { "Left Input Mixer", "Boost Switch", "LINPUT1" }, /* Really Boost Switch */ { "Left Input Mixer", NULL, "LINPUT2" }, { "Left Input Mixer", NULL, "LINPUT3" }, { "Right Boost Mixer", "RINPUT1 Switch", "RINPUT1" }, { "Right Boost Mixer", "RINPUT2 Switch", "RINPUT2" }, { "Right Boost Mixer", "RINPUT3 Switch", "RINPUT3" }, { "Right Input Mixer", "Boost Switch", "Right Boost Mixer" }, { "Right Input Mixer", "Boost Switch", "RINPUT1" }, /* Really Boost Switch */ { "Right Input Mixer", NULL, "RINPUT2" }, { "Right Input Mixer", NULL, "RINPUT3" }, { "Left ADC", NULL, "Left Input Mixer" }, { "Right ADC", NULL, "Right Input Mixer" }, { "Left Output Mixer", "LINPUT3 Switch", "LINPUT3" }, { "Left Output Mixer", "Boost Bypass Switch", "Left Boost Mixer" }, { "Left Output Mixer", "PCM Playback Switch", "Left DAC" }, { "Right Output Mixer", "RINPUT3 Switch", "RINPUT3" }, { "Right Output Mixer", "Boost Bypass Switch", "Right Boost Mixer" }, { "Right Output Mixer", "PCM Playback Switch", "Right DAC" }, { "LOUT1 PGA", NULL, "Left Output Mixer" }, { "ROUT1 PGA", NULL, "Right Output Mixer" }, { "HP_L", NULL, "LOUT1 PGA" }, { "HP_R", NULL, "ROUT1 PGA" }, { "Left Speaker PGA", NULL, "Left Output Mixer" }, { "Right Speaker PGA", NULL, "Right Output Mixer" }, { "Left Speaker Output", NULL, "Left Speaker PGA" }, { "Right Speaker Output", NULL, "Right Speaker PGA" }, { "SPK_LN", NULL, "Left Speaker Output" }, { "SPK_LP", NULL, "Left Speaker Output" }, { "SPK_RN", NULL, "Right Speaker Output" }, { "SPK_RP", NULL, "Right Speaker Output" }, }; static const struct snd_soc_dapm_route audio_paths_out3[] = { { "Mono Output Mixer", "Left Switch", "Left Output Mixer" }, { "Mono Output Mixer", "Right Switch", "Right Output Mixer" }, { "OUT3", NULL, "Mono Output Mixer", } }; static const struct snd_soc_dapm_route audio_paths_capless[] = { { "HP_L", NULL, "OUT3 VMID" }, { "HP_R", NULL, "OUT3 VMID" }, { "OUT3 VMID", NULL, "Left Output Mixer" }, { "OUT3 VMID", NULL, "Right Output Mixer" }, };
static int wm8960_add_widgets(struct snd_soc_codec *codec) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); struct wm8960_data *pdata = &wm8960->pdata; struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec); struct snd_soc_dapm_widget *w; snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets, ARRAY_SIZE(wm8960_dapm_widgets)); snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths)); /* In capless mode OUT3 is used to provide VMID for the * headphone outputs, otherwise it is used as a mono mixer. */ if (pdata && pdata->capless) { snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_capless, ARRAY_SIZE(wm8960_dapm_widgets_capless)); snd_soc_dapm_add_routes(dapm, audio_paths_capless, ARRAY_SIZE(audio_paths_capless)); } else { snd_soc_dapm_new_controls(dapm, wm8960_dapm_widgets_out3, ARRAY_SIZE(wm8960_dapm_widgets_out3)); snd_soc_dapm_add_routes(dapm, audio_paths_out3, ARRAY_SIZE(audio_paths_out3)); } /* We need to power up the headphone output stage out of * sequence for capless mode. To save scanning the widget * list each time to find the desired power state do so now * and save the result. */ list_for_each_entry(w, &codec->component.card->widgets, list) { if (w->dapm != dapm) continue; if (strcmp(w->name, "LOUT1 PGA") == 0) wm8960->lout1 = w; if (strcmp(w->name, "ROUT1 PGA") == 0) wm8960->rout1 = w; if (strcmp(w->name, "OUT3 VMID") == 0) wm8960->out3 = w; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown17683.02%337.50%
liam girdwoodliam girdwood136.13%112.50%
zidan wangzidan wang104.72%112.50%
jarkko nikulajarkko nikula94.25%112.50%
lars-peter clausenlars-peter clausen41.89%225.00%
Total212100.00%8100.00%


static int wm8960_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) { struct snd_soc_codec *codec = codec_dai->codec; u16 iface = 0; /* set master/slave audio interface */ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: iface |= 0x0040; break; case SND_SOC_DAIFMT_CBS_CFS: break; default: return -EINVAL; } /* interface format */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: iface |= 0x0002; break; case SND_SOC_DAIFMT_RIGHT_J: break; case SND_SOC_DAIFMT_LEFT_J: iface |= 0x0001; break; case SND_SOC_DAIFMT_DSP_A: iface |= 0x0003; break; case SND_SOC_DAIFMT_DSP_B: iface |= 0x0013; break; default: return -EINVAL; } /* clock inversion */ switch (fmt & SND_SOC_DAIFMT_INV_MASK) { case SND_SOC_DAIFMT_NB_NF: break; case SND_SOC_DAIFMT_IB_IF: iface |= 0x0090; break; case SND_SOC_DAIFMT_IB_NF: iface |= 0x0080; break; case SND_SOC_DAIFMT_NB_IF: iface |= 0x0010; break; default: return -EINVAL; } /* set iface */ snd_soc_write(codec, WM8960_IFACE1, iface); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown160100.00%2100.00%
Total160100.00%2100.00%

static struct { int rate; unsigned int val; } alc_rates[] = { { 48000, 0 }, { 44100, 0 }, { 32000, 1 }, { 22050, 2 }, { 24000, 2 }, { 16000, 3 }, { 11025, 4 }, { 12000, 4 }, { 8000, 5 }, }; /* -1 for reserved value */ static const int sysclk_divs[] = { 1, -1, 2, -1 }; /* Multiply 256 for internal 256 div */ static const int dac_divs[] = { 256, 384, 512, 768, 1024, 1408, 1536 }; /* Multiply 10 to eliminate decimials */ static const int bclk_divs[] = { 10, 15, 20, 30, 40, 55, 60, 80, 110, 120, 160, 220, 240, 320, 320, 320 };
static int wm8960_configure_clocking(struct snd_soc_codec *codec) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); int sysclk, bclk, lrclk, freq_out, freq_in; u16 iface1 = snd_soc_read(codec, WM8960_IFACE1); int i, j, k; if (!(iface1 & (1<<6))) { dev_dbg(codec->dev, "Codec is slave mode, no need to configure clock\n"); return 0; } if (wm8960->clk_id != WM8960_SYSCLK_MCLK && !wm8960->freq_in) { dev_err(codec->dev, "No MCLK configured\n"); return -EINVAL; } freq_in = wm8960->freq_in; bclk = wm8960->bclk; lrclk = wm8960->lrclk; /* * If it's sysclk auto mode, check if the MCLK can provide sysclk or * not. If MCLK can provide sysclk, using MCLK to provide sysclk * directly. Otherwise, auto select a available pll out frequency * and set PLL. */ if (wm8960->clk_id == WM8960_SYSCLK_AUTO) { /* disable the PLL and using MCLK to provide sysclk */ wm8960_set_pll(codec, 0, 0); freq_out = freq_in; } else if (wm8960->sysclk) { freq_out = wm8960->sysclk; } else { dev_err(codec->dev, "No SYSCLK configured\n"); return -EINVAL; } if (wm8960->clk_id != WM8960_SYSCLK_PLL) { /* check if the sysclk frequency is available. */ for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) { if (sysclk_divs[i] == -1) continue; sysclk = freq_out / sysclk_divs[i]; for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) { if (sysclk != dac_divs[j] * lrclk) continue; for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) if (sysclk == bclk * bclk_divs[k] / 10) break; if (k != ARRAY_SIZE(bclk_divs)) break; } if (j != ARRAY_SIZE(dac_divs)) break; } if (i != ARRAY_SIZE(sysclk_divs)) { goto configure_clock; } else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) { dev_err(codec->dev, "failed to configure clock\n"); return -EINVAL; } } /* get a available pll out frequency and set pll */ for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) { if (sysclk_divs[i] == -1) continue; for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) { sysclk = lrclk * dac_divs[j]; freq_out = sysclk * sysclk_divs[i]; for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) { if (sysclk == bclk * bclk_divs[k] / 10 && is_pll_freq_available(freq_in, freq_out)) { wm8960_set_pll(codec, freq_in, freq_out); break; } else { continue; } } if (k != ARRAY_SIZE(bclk_divs)) break; } if (j != ARRAY_SIZE(dac_divs)) break; } if (i == ARRAY_SIZE(sysclk_divs)) { dev_err(codec->dev, "failed to configure clock\n"); return -EINVAL; } configure_clock: /* configure sysclk clock */ snd_soc_update_bits(codec, WM8960_CLOCK1, 3 << 1, i << 1); /* configure frame clock */ snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 3, j << 3); snd_soc_update_bits(codec, WM8960_CLOCK1, 0x7 << 6, j << 6); /* configure bit clock */ snd_soc_update_bits(codec, WM8960_CLOCK2, 0xf, k); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
zidan wangzidan wang56197.91%266.67%
stuart hendersonstuart henderson122.09%133.33%
Total573100.00%3100.00%


static int wm8960_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 wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); u16 iface = snd_soc_read(codec, WM8960_IFACE1) & 0xfff3; bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; int i; wm8960->bclk = snd_soc_params_to_bclk(params); if (params_channels(params) == 1) wm8960->bclk *= 2; /* bit size */ switch (params_width(params)) { case 16: break; case 20: iface |= 0x0004; break; case 24: iface |= 0x0008; break; case 32: /* right justify mode does not support 32 word length */ if ((iface & 0x3) != 0) { iface |= 0x000c; break; } default: dev_err(codec->dev, "unsupported width %d\n", params_width(params)); return -EINVAL; } wm8960->lrclk = params_rate(params); /* Update filters for the new rate */ if (tx) { wm8960_set_deemph(codec); } else { for (i = 0; i < ARRAY_SIZE(alc_rates); i++) if (alc_rates[i].rate == params_rate(params)) snd_soc_update_bits(codec, WM8960_ADDCTL3, 0x7, alc_rates[i].val); } /* set iface */ snd_soc_write(codec, WM8960_IFACE1, iface); wm8960->is_stream_in_use[tx] = true; if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON && !wm8960->is_stream_in_use[!tx]) return wm8960_configure_clocking(codec); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown15958.03%660.00%
zidan wangzidan wang9835.77%330.00%
timur tabitimur tabi176.20%110.00%
Total274100.00%10100.00%


static int wm8960_hw_free(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_codec *codec = dai->codec; struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; wm8960->is_stream_in_use[tx] = false; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
zidan wangzidan wang5292.86%266.67%
mark brownmark brown47.14%133.33%
Total56100.00%3100.00%


static int wm8960_mute(struct snd_soc_dai *dai, int mute) { struct snd_soc_codec *codec = dai->codec; if (mute) snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0x8); else snd_soc_update_bits(codec, WM8960_DACCTL1, 0x8, 0); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown4686.79%150.00%
axel linaxel lin713.21%150.00%
Total53100.00%2100.00%


static int wm8960_set_bias_level_out3(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); u16 pm2 = snd_soc_read(codec, WM8960_POWER2); int ret; switch (level) { case SND_SOC_BIAS_ON: break; case SND_SOC_BIAS_PREPARE: switch (snd_soc_codec_get_bias_level(codec)) { case SND_SOC_BIAS_STANDBY: if (!IS_ERR(wm8960->mclk)) { ret = clk_prepare_enable(wm8960->mclk); if (ret) { dev_err(codec->dev, "Failed to enable MCLK: %d\n", ret); return ret; } } ret = wm8960_configure_clocking(codec); if (ret) return ret; /* Set VMID to 2x50k */ snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x80); break; case SND_SOC_BIAS_ON: /* * If it's sysclk auto mode, and the pll is enabled, * disable the pll */ if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1)) wm8960_set_pll(codec, 0, 0); if (!IS_ERR(wm8960->mclk)) clk_disable_unprepare(wm8960->mclk); break; default: break; } break; case SND_SOC_BIAS_STANDBY: if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) { regcache_sync(wm8960->regmap); /* Enable anti-pop features */ snd_soc_write(codec, WM8960_APOP1, WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_BUFDCOPEN | WM8960_BUFIOEN); /* Enable & ramp VMID at 2x50k */ snd_soc_update_bits(codec, WM8960_POWER1, 0x80, 0x80); msleep(100); /* Enable VREF */ snd_soc_update_bits(codec, WM8960_POWER1, WM8960_VREF, WM8960_VREF); /* Disable anti-pop features */ snd_soc_write(codec, WM8960_APOP1, WM8960_BUFIOEN); } /* Set VMID to 2x250k */ snd_soc_update_bits(codec, WM8960_POWER1, 0x180, 0x100); break; case SND_SOC_BIAS_OFF: /* Enable anti-pop features */ snd_soc_write(codec, WM8960_APOP1, WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_BUFDCOPEN | WM8960_BUFIOEN); /* Disable VMID and VREF, let them discharge */ snd_soc_write(codec, WM8960_POWER1, 0); msleep(600); break; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown16052.29%444.44%
zidan wangzidan wang12540.85%222.22%
axel linaxel lin154.90%222.22%
lars-peter clausenlars-peter clausen61.96%111.11%
Total306100.00%9100.00%


static int wm8960_set_bias_level_capless(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); u16 pm2 = snd_soc_read(codec, WM8960_POWER2); int reg, ret; switch (level) { case SND_SOC_BIAS_ON: break; case SND_SOC_BIAS_PREPARE: switch (snd_soc_codec_get_bias_level(codec)) { case SND_SOC_BIAS_STANDBY: /* Enable anti pop mode */ snd_soc_update_bits(codec, WM8960_APOP1, WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_BUFDCOPEN, WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_BUFDCOPEN); /* Enable LOUT1, ROUT1 and OUT3 if they're enabled */ reg = 0; if (wm8960->lout1 && wm8960->lout1->power) reg |= WM8960_PWR2_LOUT1; if (wm8960->rout1 && wm8960->rout1->power) reg |= WM8960_PWR2_ROUT1; if (wm8960->out3 && wm8960->out3->power) reg |= WM8960_PWR2_OUT3; snd_soc_update_bits(codec, WM8960_POWER2, WM8960_PWR2_LOUT1 | WM8960_PWR2_ROUT1 | WM8960_PWR2_OUT3, reg); /* Enable VMID at 2*50k */ snd_soc_update_bits(codec, WM8960_POWER1, WM8960_VMID_MASK, 0x80); /* Ramp */ msleep(100); /* Enable VREF */ snd_soc_update_bits(codec, WM8960_POWER1, WM8960_VREF, WM8960_VREF); msleep(100); if (!IS_ERR(wm8960->mclk)) { ret = clk_prepare_enable(wm8960->mclk); if (ret) { dev_err(codec->dev, "Failed to enable MCLK: %d\n", ret); return ret; } } ret = wm8960_configure_clocking(codec); if (ret) return ret; break; case SND_SOC_BIAS_ON: /* * If it's sysclk auto mode, and the pll is enabled, * disable the pll */ if (wm8960->clk_id == WM8960_SYSCLK_AUTO && (pm2 & 0x1)) wm8960_set_pll(codec, 0, 0); if (!IS_ERR(wm8960->mclk)) clk_disable_unprepare(wm8960->mclk); /* Enable anti-pop mode */ snd_soc_update_bits(codec, WM8960_APOP1, WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_BUFDCOPEN, WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_BUFDCOPEN); /* Disable VMID and VREF */ snd_soc_update_bits(codec, WM8960_POWER1, WM8960_VREF | WM8960_VMID_MASK, 0); break; case SND_SOC_BIAS_OFF: regcache_sync(wm8960->regmap); break; default: break; } break; case SND_SOC_BIAS_STANDBY: switch (snd_soc_codec_get_bias_level(codec)) { case SND_SOC_BIAS_PREPARE: /* Disable HP discharge */ snd_soc_update_bits(codec, WM8960_APOP2, WM8960_DISOP | WM8960_DRES_MASK, 0); /* Disable anti-pop features */ snd_soc_update_bits(codec, WM8960_APOP1, WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_BUFDCOPEN, WM8960_POBCTRL | WM8960_SOFT_ST | WM8960_BUFDCOPEN); break; default: break; } break; case SND_SOC_BIAS_OFF: break; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown27669.52%450.00%
zidan wangzidan wang10827.20%225.00%
axel linaxel lin71.76%112.50%
lars-peter clausenlars-peter clausen61.51%112.50%
Total397100.00%8100.00%

/* PLL divisors */ struct _pll_div { u32 pre_div:1; u32 n:4; u32 k:24; };
static bool is_pll_freq_available(unsigned int source, unsigned int target) { unsigned int Ndiv; if (source == 0 || target == 0) return false; /* Scale up target to PLL operating frequency */ target *= 4; Ndiv = target / source; if (Ndiv < 6) { source >>= 1; Ndiv = target / source; } if ((Ndiv < 6) || (Ndiv > 12)) return false; return true; }

Contributors

PersonTokensPropCommitsCommitProp
zidan wangzidan wang80100.00%1100.00%
Total80100.00%1100.00%

/* The size in bits of the pll divide multiplied by 10 * to allow rounding later */ #define FIXED_PLL_SIZE ((1 << 24) * 10)
static int pll_factors(unsigned int source, unsigned int target, struct _pll_div *pll_div) { unsigned long long Kpart; unsigned int K, Ndiv, Nmod; pr_debug("WM8960 PLL: setting %dHz->%dHz\n", source, target); /* Scale up target to PLL operating frequency */ target *= 4; Ndiv = target / source; if (Ndiv < 6) { source >>= 1; pll_div->pre_div = 1; Ndiv = target / source; } else pll_div->pre_div = 0; if ((Ndiv < 6) || (Ndiv > 12)) { pr_err("WM8960 PLL: Unsupported N=%d\n", Ndiv); return -EINVAL; } pll_div->n = Ndiv; Nmod = target % source; Kpart = FIXED_PLL_SIZE * (long long)Nmod; do_div(Kpart, source); K = Kpart & 0xFFFFFFFF; /* Check if we need to round */ if ((K % 10) >= 5) K += 5; /* Move down to proper range now rounding is done */ K /= 10; pll_div->k = K; pr_debug("WM8960 PLL: N=%x K=%x pre_div=%d\n", pll_div->n, pll_div->k, pll_div->pre_div); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown191100.00%1100.00%
Total191100.00%1100.00%


static int wm8960_set_pll(struct snd_soc_codec *codec, unsigned int freq_in, unsigned int freq_out) { u16 reg; static struct _pll_div pll_div; int ret; if (freq_in && freq_out) { ret = pll_factors(freq_in, freq_out, &pll_div); if (ret != 0) return ret; } /* Disable the PLL: even if we are changing the frequency the * PLL needs to be disabled while we do so. */ snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, 0); snd_soc_update_bits(codec, WM8960_POWER2, 0x1, 0); if (!freq_in || !freq_out) return 0; reg = snd_soc_read(codec, WM8960_PLL1) & ~0x3f; reg |= pll_div.pre_div << 4; reg |= pll_div.n; if (pll_div.k) { reg |= 0x20; snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 16) & 0xff); snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 8) & 0xff); snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0xff); } snd_soc_write(codec, WM8960_PLL1, reg); /* Turn it on */ snd_soc_update_bits(codec, WM8960_POWER2, 0x1, 0x1); msleep(250); snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, 0x1); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown19890.83%350.00%
axel linaxel lin125.50%116.67%
mike dyermike dyer52.29%116.67%
zidan wangzidan wang31.38%116.67%
Total218100.00%6100.00%


static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) { struct snd_soc_codec *codec = codec_dai->codec; struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); wm8960->freq_in = freq_in; if (pll_id == WM8960_SYSCLK_AUTO) return 0; return wm8960_set_pll(codec, freq_in, freq_out); }

Contributors

PersonTokensPropCommitsCommitProp
zidan wangzidan wang69100.00%1100.00%
Total69100.00%1100.00%


static int wm8960_set_dai_clkdiv(struct snd_soc_dai *codec_dai, int div_id, int div) { struct snd_soc_codec *codec = codec_dai->codec; u16 reg; switch (div_id) { case WM8960_SYSCLKDIV: reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1f9; snd_soc_write(codec, WM8960_CLOCK1, reg | div); break; case WM8960_DACDIV: reg = snd_soc_read(codec, WM8960_CLOCK1) & 0x1c7; snd_soc_write(codec, WM8960_CLOCK1, reg | div); break; case WM8960_OPCLKDIV: reg = snd_soc_read(codec, WM8960_PLL1) & 0x03f; snd_soc_write(codec, WM8960_PLL1, reg | div); break; case WM8960_DCLKDIV: reg = snd_soc_read(codec, WM8960_CLOCK2) & 0x03f; snd_soc_write(codec, WM8960_CLOCK2, reg | div); break; case WM8960_TOCLKSEL: reg = snd_soc_read(codec, WM8960_ADDCTL1) & 0x1fd; snd_soc_write(codec, WM8960_ADDCTL1, reg | div); break; default: return -EINVAL; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown173100.00%2100.00%
Total173100.00%2100.00%


static int wm8960_set_bias_level(struct snd_soc_codec *codec, enum snd_soc_bias_level level) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); return wm8960->set_bias_level(codec, level); }

Contributors

PersonTokensPropCommitsCommitProp
liam girdwoodliam girdwood35100.00%1100.00%
Total35100.00%1100.00%


static int wm8960_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_codec *codec = dai->codec; struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); switch (clk_id) { case WM8960_SYSCLK_MCLK: snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, WM8960_SYSCLK_MCLK); break; case WM8960_SYSCLK_PLL: snd_soc_update_bits(codec, WM8960_CLOCK1, 0x1, WM8960_SYSCLK_PLL); break; case WM8960_SYSCLK_AUTO: break; default: return -EINVAL; } wm8960->sysclk = freq; wm8960->clk_id = clk_id; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
zidan wangzidan wang100100.00%2100.00%
Total100100.00%2100.00%

#define WM8960_RATES SNDRV_PCM_RATE_8000_48000 #define WM8960_FORMATS \ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) static const struct snd_soc_dai_ops wm8960_dai_ops = { .hw_params = wm8960_hw_params, .hw_free = wm8960_hw_free, .digital_mute = wm8960_mute, .set_fmt = wm8960_set_dai_fmt, .set_clkdiv = wm8960_set_dai_clkdiv, .set_pll = wm8960_set_dai_pll, .set_sysclk = wm8960_set_dai_sysclk, }; static struct snd_soc_dai_driver wm8960_dai = { .name = "wm8960-hifi", .playback = { .stream_name = "Playback", .channels_min = 1, .channels_max = 2, .rates = WM8960_RATES, .formats = WM8960_FORMATS,}, .capture = { .stream_name = "Capture", .channels_min = 1, .channels_max = 2, .rates = WM8960_RATES, .formats = WM8960_FORMATS,}, .ops = &wm8960_dai_ops, .symmetric_rates = 1, };
static int wm8960_probe(struct snd_soc_codec *codec) { struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec); struct wm8960_data *pdata = &wm8960->pdata; if (pdata->capless) wm8960->set_bias_level = wm8960_set_bias_level_capless; else wm8960->set_bias_level = wm8960_set_bias_level_out3; snd_soc_add_codec_controls(codec, wm8960_snd_controls, ARRAY_SIZE(wm8960_snd_controls)); wm8960_add_widgets(codec); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown3550.00%125.00%
liam girdwoodliam girdwood2535.71%250.00%
zidan wangzidan wang1014.29%125.00%
Total70100.00%4100.00%

static struct snd_soc_codec_driver soc_codec_dev_wm8960 = { .probe = wm8960_probe, .set_bias_level = wm8960_set_bias_level, .suspend_bias_off = true, }; static const struct regmap_config wm8960_regmap = { .reg_bits = 7, .val_bits = 9, .max_register = WM8960_PLL4, .reg_defaults = wm8960_reg_defaults, .num_reg_defaults = ARRAY_SIZE(wm8960_reg_defaults), .cache_type = REGCACHE_RBTREE, .volatile_reg = wm8960_volatile, };
static void wm8960_set_pdata_from_of(struct i2c_client *i2c, struct wm8960_data *pdata) { const struct device_node *np = i2c->dev.of_node; if (of_property_read_bool(np, "wlf,capless")) pdata->capless = true; if (of_property_read_bool(np, "wlf,shared-lrclk")) pdata->shared_lrclk = true; }

Contributors

PersonTokensPropCommitsCommitProp
zidan wangzidan wang58100.00%1100.00%
Total58100.00%1100.00%


static int wm8960_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { struct wm8960_data *pdata = dev_get_platdata(&i2c->dev); struct wm8960_priv *wm8960; int ret; wm8960 = devm_kzalloc(&i2c->dev, sizeof(struct wm8960_priv), GFP_KERNEL); if (wm8960 == NULL) return -ENOMEM; wm8960->mclk = devm_clk_get(&i2c->dev, "mclk"); if (IS_ERR(wm8960->mclk)) { if (PTR_ERR(wm8960->mclk) == -EPROBE_DEFER) return -EPROBE_DEFER; } wm8960->regmap = devm_regmap_init_i2c(i2c, &wm8960_regmap); if (IS_ERR(wm8960->regmap)) return PTR_ERR(wm8960->regmap); if (pdata) memcpy(&wm8960->pdata, pdata, sizeof(struct wm8960_data)); else if (i2c->dev.of_node) wm8960_set_pdata_from_of(i2c, &wm8960->pdata); ret = wm8960_reset(wm8960->regmap); if (ret != 0) { dev_err(&i2c->dev, "Failed to issue reset\n"); return ret; } if (wm8960->pdata.shared_lrclk) { ret = regmap_update_bits(wm8960->regmap, WM8960_ADDCTL2, 0x4, 0x4); if (ret != 0) { dev_err(&i2c->dev, "Failed to enable LRCM: %d\n", ret); return ret; } } /* Latch the update bits */ regmap_update_bits(wm8960->regmap, WM8960_LINVOL, 0x100, 0x100); regmap_update_bits(wm8960->regmap, WM8960_RINVOL, 0x100, 0x100); regmap_update_bits(wm8960->regmap, WM8960_LADC, 0x100, 0x100); regmap_update_bits(wm8960->regmap, WM8960_RADC, 0x100, 0x100); regmap_update_bits(wm8960->regmap, WM8960_LDAC, 0x100, 0x100); regmap_update_bits(wm8960->regmap, WM8960_RDAC, 0x100, 0x100); regmap_update_bits(wm8960->regmap, WM8960_LOUT1, 0x100, 0x100); regmap_update_bits(wm8960->regmap, WM8960_ROUT1, 0x100, 0x100); regmap_update_bits(wm8960->regmap, WM8960_LOUT2, 0x100, 0x100); regmap_update_bits(wm8960->regmap, WM8960_ROUT2, 0x100, 0x100); i2c_set_clientdata(i2c, wm8960); ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_wm8960, &wm8960_dai, 1); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
zidan wangzidan wang24358.98%330.00%
mark brownmark brown15036.41%440.00%
liam girdwoodliam girdwood174.13%110.00%
sachin kamatsachin kamat10.24%110.00%
lars-peter clausenlars-peter clausen10.24%110.00%
Total412100.00%10100.00%


static int wm8960_i2c_remove(struct i2c_client *client) { snd_soc_unregister_codec(&client->dev); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown1777.27%150.00%
liam girdwoodliam girdwood522.73%150.00%
Total22100.00%2100.00%

static const struct i2c_device_id wm8960_i2c_id[] = { { "wm8960", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id); static const struct of_device_id wm8960_of_match[] = { { .compatible = "wlf,wm8960", }, { } }; MODULE_DEVICE_TABLE(of, wm8960_of_match); static struct i2c_driver wm8960_i2c_driver = { .driver = { .name = "wm8960", .of_match_table = wm8960_of_match, }, .probe = wm8960_i2c_probe, .remove = wm8960_i2c_remove, .id_table = wm8960_i2c_id, }; module_i2c_driver(wm8960_i2c_driver); MODULE_DESCRIPTION("ASoC WM8960 driver"); MODULE_AUTHOR("Liam Girdwood"); MODULE_LICENSE("GPL");

Overall Contributors

PersonTokensPropCommitsCommitProp
mark brownmark brown453067.78%2033.90%
zidan wangzidan wang187228.01%1525.42%
liam girdwoodliam girdwood1402.09%35.08%
axel linaxel lin410.61%23.39%
lars-peter clausenlars-peter clausen240.36%610.17%
timur tabitimur tabi170.25%11.69%
stuart hendersonstuart henderson160.24%23.39%
dmitry artamonowdmitry artamonow120.18%11.69%
jarkko nikulajarkko nikula90.13%11.69%
ma haijunma haijun60.09%11.69%
mike dyermike dyer50.07%11.69%
takashi iwaitakashi iwai40.06%11.69%
tejun heotejun heo30.04%11.69%
sachin kamatsachin kamat20.03%23.39%
jongho kimjongho kim10.01%11.69%
dan carpenterdan carpenter10.01%11.69%
Total6683100.00%59100.00%
Directory: sound/soc/codecs
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}