162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * wm8580.c -- WM8580 and WM8581 ALSA Soc Audio driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright 2008-12 Wolfson Microelectronics PLC. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Notes: 862306a36Sopenharmony_ci * The WM8580 is a multichannel codec with S/PDIF support, featuring six 962306a36Sopenharmony_ci * DAC channels and two ADC channels. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * The WM8581 is a multichannel codec with S/PDIF support, featuring eight 1262306a36Sopenharmony_ci * DAC channels and two ADC channels. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * Currently only the primary audio interface is supported - S/PDIF and 1562306a36Sopenharmony_ci * the secondary audio interfaces are not. 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include <linux/module.h> 1962306a36Sopenharmony_ci#include <linux/moduleparam.h> 2062306a36Sopenharmony_ci#include <linux/kernel.h> 2162306a36Sopenharmony_ci#include <linux/init.h> 2262306a36Sopenharmony_ci#include <linux/delay.h> 2362306a36Sopenharmony_ci#include <linux/pm.h> 2462306a36Sopenharmony_ci#include <linux/i2c.h> 2562306a36Sopenharmony_ci#include <linux/regmap.h> 2662306a36Sopenharmony_ci#include <linux/regulator/consumer.h> 2762306a36Sopenharmony_ci#include <linux/slab.h> 2862306a36Sopenharmony_ci#include <linux/of_device.h> 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#include <sound/core.h> 3162306a36Sopenharmony_ci#include <sound/pcm.h> 3262306a36Sopenharmony_ci#include <sound/pcm_params.h> 3362306a36Sopenharmony_ci#include <sound/soc.h> 3462306a36Sopenharmony_ci#include <sound/tlv.h> 3562306a36Sopenharmony_ci#include <sound/initval.h> 3662306a36Sopenharmony_ci#include <asm/div64.h> 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include "wm8580.h" 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* WM8580 register space */ 4162306a36Sopenharmony_ci#define WM8580_PLLA1 0x00 4262306a36Sopenharmony_ci#define WM8580_PLLA2 0x01 4362306a36Sopenharmony_ci#define WM8580_PLLA3 0x02 4462306a36Sopenharmony_ci#define WM8580_PLLA4 0x03 4562306a36Sopenharmony_ci#define WM8580_PLLB1 0x04 4662306a36Sopenharmony_ci#define WM8580_PLLB2 0x05 4762306a36Sopenharmony_ci#define WM8580_PLLB3 0x06 4862306a36Sopenharmony_ci#define WM8580_PLLB4 0x07 4962306a36Sopenharmony_ci#define WM8580_CLKSEL 0x08 5062306a36Sopenharmony_ci#define WM8580_PAIF1 0x09 5162306a36Sopenharmony_ci#define WM8580_PAIF2 0x0A 5262306a36Sopenharmony_ci#define WM8580_SAIF1 0x0B 5362306a36Sopenharmony_ci#define WM8580_PAIF3 0x0C 5462306a36Sopenharmony_ci#define WM8580_PAIF4 0x0D 5562306a36Sopenharmony_ci#define WM8580_SAIF2 0x0E 5662306a36Sopenharmony_ci#define WM8580_DAC_CONTROL1 0x0F 5762306a36Sopenharmony_ci#define WM8580_DAC_CONTROL2 0x10 5862306a36Sopenharmony_ci#define WM8580_DAC_CONTROL3 0x11 5962306a36Sopenharmony_ci#define WM8580_DAC_CONTROL4 0x12 6062306a36Sopenharmony_ci#define WM8580_DAC_CONTROL5 0x13 6162306a36Sopenharmony_ci#define WM8580_DIGITAL_ATTENUATION_DACL1 0x14 6262306a36Sopenharmony_ci#define WM8580_DIGITAL_ATTENUATION_DACR1 0x15 6362306a36Sopenharmony_ci#define WM8580_DIGITAL_ATTENUATION_DACL2 0x16 6462306a36Sopenharmony_ci#define WM8580_DIGITAL_ATTENUATION_DACR2 0x17 6562306a36Sopenharmony_ci#define WM8580_DIGITAL_ATTENUATION_DACL3 0x18 6662306a36Sopenharmony_ci#define WM8580_DIGITAL_ATTENUATION_DACR3 0x19 6762306a36Sopenharmony_ci#define WM8581_DIGITAL_ATTENUATION_DACL4 0x1A 6862306a36Sopenharmony_ci#define WM8581_DIGITAL_ATTENUATION_DACR4 0x1B 6962306a36Sopenharmony_ci#define WM8580_MASTER_DIGITAL_ATTENUATION 0x1C 7062306a36Sopenharmony_ci#define WM8580_ADC_CONTROL1 0x1D 7162306a36Sopenharmony_ci#define WM8580_SPDTXCHAN0 0x1E 7262306a36Sopenharmony_ci#define WM8580_SPDTXCHAN1 0x1F 7362306a36Sopenharmony_ci#define WM8580_SPDTXCHAN2 0x20 7462306a36Sopenharmony_ci#define WM8580_SPDTXCHAN3 0x21 7562306a36Sopenharmony_ci#define WM8580_SPDTXCHAN4 0x22 7662306a36Sopenharmony_ci#define WM8580_SPDTXCHAN5 0x23 7762306a36Sopenharmony_ci#define WM8580_SPDMODE 0x24 7862306a36Sopenharmony_ci#define WM8580_INTMASK 0x25 7962306a36Sopenharmony_ci#define WM8580_GPO1 0x26 8062306a36Sopenharmony_ci#define WM8580_GPO2 0x27 8162306a36Sopenharmony_ci#define WM8580_GPO3 0x28 8262306a36Sopenharmony_ci#define WM8580_GPO4 0x29 8362306a36Sopenharmony_ci#define WM8580_GPO5 0x2A 8462306a36Sopenharmony_ci#define WM8580_INTSTAT 0x2B 8562306a36Sopenharmony_ci#define WM8580_SPDRXCHAN1 0x2C 8662306a36Sopenharmony_ci#define WM8580_SPDRXCHAN2 0x2D 8762306a36Sopenharmony_ci#define WM8580_SPDRXCHAN3 0x2E 8862306a36Sopenharmony_ci#define WM8580_SPDRXCHAN4 0x2F 8962306a36Sopenharmony_ci#define WM8580_SPDRXCHAN5 0x30 9062306a36Sopenharmony_ci#define WM8580_SPDSTAT 0x31 9162306a36Sopenharmony_ci#define WM8580_PWRDN1 0x32 9262306a36Sopenharmony_ci#define WM8580_PWRDN2 0x33 9362306a36Sopenharmony_ci#define WM8580_READBACK 0x34 9462306a36Sopenharmony_ci#define WM8580_RESET 0x35 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci#define WM8580_MAX_REGISTER 0x35 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define WM8580_DACOSR 0x40 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/* PLLB4 (register 7h) */ 10162306a36Sopenharmony_ci#define WM8580_PLLB4_MCLKOUTSRC_MASK 0x60 10262306a36Sopenharmony_ci#define WM8580_PLLB4_MCLKOUTSRC_PLLA 0x20 10362306a36Sopenharmony_ci#define WM8580_PLLB4_MCLKOUTSRC_PLLB 0x40 10462306a36Sopenharmony_ci#define WM8580_PLLB4_MCLKOUTSRC_OSC 0x60 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci#define WM8580_PLLB4_CLKOUTSRC_MASK 0x180 10762306a36Sopenharmony_ci#define WM8580_PLLB4_CLKOUTSRC_PLLACLK 0x080 10862306a36Sopenharmony_ci#define WM8580_PLLB4_CLKOUTSRC_PLLBCLK 0x100 10962306a36Sopenharmony_ci#define WM8580_PLLB4_CLKOUTSRC_OSCCLK 0x180 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* CLKSEL (register 8h) */ 11262306a36Sopenharmony_ci#define WM8580_CLKSEL_DAC_CLKSEL_MASK 0x03 11362306a36Sopenharmony_ci#define WM8580_CLKSEL_DAC_CLKSEL_PLLA 0x01 11462306a36Sopenharmony_ci#define WM8580_CLKSEL_DAC_CLKSEL_PLLB 0x02 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* AIF control 1 (registers 9h-bh) */ 11762306a36Sopenharmony_ci#define WM8580_AIF_RATE_MASK 0x7 11862306a36Sopenharmony_ci#define WM8580_AIF_BCLKSEL_MASK 0x18 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci#define WM8580_AIF_MS 0x20 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_ci#define WM8580_AIF_CLKSRC_MASK 0xc0 12362306a36Sopenharmony_ci#define WM8580_AIF_CLKSRC_PLLA 0x40 12462306a36Sopenharmony_ci#define WM8580_AIF_CLKSRC_PLLB 0x40 12562306a36Sopenharmony_ci#define WM8580_AIF_CLKSRC_MCLK 0xc0 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/* AIF control 2 (registers ch-eh) */ 12862306a36Sopenharmony_ci#define WM8580_AIF_FMT_MASK 0x03 12962306a36Sopenharmony_ci#define WM8580_AIF_FMT_RIGHTJ 0x00 13062306a36Sopenharmony_ci#define WM8580_AIF_FMT_LEFTJ 0x01 13162306a36Sopenharmony_ci#define WM8580_AIF_FMT_I2S 0x02 13262306a36Sopenharmony_ci#define WM8580_AIF_FMT_DSP 0x03 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_ci#define WM8580_AIF_LENGTH_MASK 0x0c 13562306a36Sopenharmony_ci#define WM8580_AIF_LENGTH_16 0x00 13662306a36Sopenharmony_ci#define WM8580_AIF_LENGTH_20 0x04 13762306a36Sopenharmony_ci#define WM8580_AIF_LENGTH_24 0x08 13862306a36Sopenharmony_ci#define WM8580_AIF_LENGTH_32 0x0c 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci#define WM8580_AIF_LRP 0x10 14162306a36Sopenharmony_ci#define WM8580_AIF_BCP 0x20 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci/* Powerdown Register 1 (register 32h) */ 14462306a36Sopenharmony_ci#define WM8580_PWRDN1_PWDN 0x001 14562306a36Sopenharmony_ci#define WM8580_PWRDN1_ALLDACPD 0x040 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci/* Powerdown Register 2 (register 33h) */ 14862306a36Sopenharmony_ci#define WM8580_PWRDN2_OSSCPD 0x001 14962306a36Sopenharmony_ci#define WM8580_PWRDN2_PLLAPD 0x002 15062306a36Sopenharmony_ci#define WM8580_PWRDN2_PLLBPD 0x004 15162306a36Sopenharmony_ci#define WM8580_PWRDN2_SPDIFPD 0x008 15262306a36Sopenharmony_ci#define WM8580_PWRDN2_SPDIFTXD 0x010 15362306a36Sopenharmony_ci#define WM8580_PWRDN2_SPDIFRXD 0x020 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci#define WM8580_DAC_CONTROL5_MUTEALL 0x10 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci/* 15862306a36Sopenharmony_ci * wm8580 register cache 15962306a36Sopenharmony_ci * We can't read the WM8580 register space when we 16062306a36Sopenharmony_ci * are using 2 wire for device control, so we cache them instead. 16162306a36Sopenharmony_ci */ 16262306a36Sopenharmony_cistatic const struct reg_default wm8580_reg_defaults[] = { 16362306a36Sopenharmony_ci { 0, 0x0121 }, 16462306a36Sopenharmony_ci { 1, 0x017e }, 16562306a36Sopenharmony_ci { 2, 0x007d }, 16662306a36Sopenharmony_ci { 3, 0x0014 }, 16762306a36Sopenharmony_ci { 4, 0x0121 }, 16862306a36Sopenharmony_ci { 5, 0x017e }, 16962306a36Sopenharmony_ci { 6, 0x007d }, 17062306a36Sopenharmony_ci { 7, 0x0194 }, 17162306a36Sopenharmony_ci { 8, 0x0010 }, 17262306a36Sopenharmony_ci { 9, 0x0002 }, 17362306a36Sopenharmony_ci { 10, 0x0002 }, 17462306a36Sopenharmony_ci { 11, 0x00c2 }, 17562306a36Sopenharmony_ci { 12, 0x0182 }, 17662306a36Sopenharmony_ci { 13, 0x0082 }, 17762306a36Sopenharmony_ci { 14, 0x000a }, 17862306a36Sopenharmony_ci { 15, 0x0024 }, 17962306a36Sopenharmony_ci { 16, 0x0009 }, 18062306a36Sopenharmony_ci { 17, 0x0000 }, 18162306a36Sopenharmony_ci { 18, 0x00ff }, 18262306a36Sopenharmony_ci { 19, 0x0000 }, 18362306a36Sopenharmony_ci { 20, 0x00ff }, 18462306a36Sopenharmony_ci { 21, 0x00ff }, 18562306a36Sopenharmony_ci { 22, 0x00ff }, 18662306a36Sopenharmony_ci { 23, 0x00ff }, 18762306a36Sopenharmony_ci { 24, 0x00ff }, 18862306a36Sopenharmony_ci { 25, 0x00ff }, 18962306a36Sopenharmony_ci { 26, 0x00ff }, 19062306a36Sopenharmony_ci { 27, 0x00ff }, 19162306a36Sopenharmony_ci { 28, 0x01f0 }, 19262306a36Sopenharmony_ci { 29, 0x0040 }, 19362306a36Sopenharmony_ci { 30, 0x0000 }, 19462306a36Sopenharmony_ci { 31, 0x0000 }, 19562306a36Sopenharmony_ci { 32, 0x0000 }, 19662306a36Sopenharmony_ci { 33, 0x0000 }, 19762306a36Sopenharmony_ci { 34, 0x0031 }, 19862306a36Sopenharmony_ci { 35, 0x000b }, 19962306a36Sopenharmony_ci { 36, 0x0039 }, 20062306a36Sopenharmony_ci { 37, 0x0000 }, 20162306a36Sopenharmony_ci { 38, 0x0010 }, 20262306a36Sopenharmony_ci { 39, 0x0032 }, 20362306a36Sopenharmony_ci { 40, 0x0054 }, 20462306a36Sopenharmony_ci { 41, 0x0076 }, 20562306a36Sopenharmony_ci { 42, 0x0098 }, 20662306a36Sopenharmony_ci { 43, 0x0000 }, 20762306a36Sopenharmony_ci { 44, 0x0000 }, 20862306a36Sopenharmony_ci { 45, 0x0000 }, 20962306a36Sopenharmony_ci { 46, 0x0000 }, 21062306a36Sopenharmony_ci { 47, 0x0000 }, 21162306a36Sopenharmony_ci { 48, 0x0000 }, 21262306a36Sopenharmony_ci { 49, 0x0000 }, 21362306a36Sopenharmony_ci { 50, 0x005e }, 21462306a36Sopenharmony_ci { 51, 0x003e }, 21562306a36Sopenharmony_ci { 52, 0x0000 }, 21662306a36Sopenharmony_ci}; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_cistatic bool wm8580_volatile(struct device *dev, unsigned int reg) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci switch (reg) { 22162306a36Sopenharmony_ci case WM8580_RESET: 22262306a36Sopenharmony_ci return true; 22362306a36Sopenharmony_ci default: 22462306a36Sopenharmony_ci return false; 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistruct pll_state { 22962306a36Sopenharmony_ci unsigned int in; 23062306a36Sopenharmony_ci unsigned int out; 23162306a36Sopenharmony_ci}; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci#define WM8580_NUM_SUPPLIES 3 23462306a36Sopenharmony_cistatic const char *wm8580_supply_names[WM8580_NUM_SUPPLIES] = { 23562306a36Sopenharmony_ci "AVDD", 23662306a36Sopenharmony_ci "DVDD", 23762306a36Sopenharmony_ci "PVDD", 23862306a36Sopenharmony_ci}; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_cistruct wm8580_driver_data { 24162306a36Sopenharmony_ci int num_dacs; 24262306a36Sopenharmony_ci}; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci/* codec private data */ 24562306a36Sopenharmony_cistruct wm8580_priv { 24662306a36Sopenharmony_ci struct regmap *regmap; 24762306a36Sopenharmony_ci struct regulator_bulk_data supplies[WM8580_NUM_SUPPLIES]; 24862306a36Sopenharmony_ci struct pll_state a; 24962306a36Sopenharmony_ci struct pll_state b; 25062306a36Sopenharmony_ci const struct wm8580_driver_data *drvdata; 25162306a36Sopenharmony_ci int sysclk[2]; 25262306a36Sopenharmony_ci}; 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic const DECLARE_TLV_DB_SCALE(dac_tlv, -12750, 50, 1); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic int wm8580_out_vu(struct snd_kcontrol *kcontrol, 25762306a36Sopenharmony_ci struct snd_ctl_elem_value *ucontrol) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci struct soc_mixer_control *mc = 26062306a36Sopenharmony_ci (struct soc_mixer_control *)kcontrol->private_value; 26162306a36Sopenharmony_ci struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); 26262306a36Sopenharmony_ci struct wm8580_priv *wm8580 = snd_soc_component_get_drvdata(component); 26362306a36Sopenharmony_ci unsigned int reg = mc->reg; 26462306a36Sopenharmony_ci unsigned int reg2 = mc->rreg; 26562306a36Sopenharmony_ci int ret; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* Clear the register cache VU so we write without VU set */ 26862306a36Sopenharmony_ci regcache_cache_only(wm8580->regmap, true); 26962306a36Sopenharmony_ci regmap_update_bits(wm8580->regmap, reg, 0x100, 0x000); 27062306a36Sopenharmony_ci regmap_update_bits(wm8580->regmap, reg2, 0x100, 0x000); 27162306a36Sopenharmony_ci regcache_cache_only(wm8580->regmap, false); 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci ret = snd_soc_put_volsw(kcontrol, ucontrol); 27462306a36Sopenharmony_ci if (ret < 0) 27562306a36Sopenharmony_ci return ret; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci /* Now write again with the volume update bit set */ 27862306a36Sopenharmony_ci snd_soc_component_update_bits(component, reg, 0x100, 0x100); 27962306a36Sopenharmony_ci snd_soc_component_update_bits(component, reg2, 0x100, 0x100); 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci return 0; 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic const struct snd_kcontrol_new wm8580_snd_controls[] = { 28562306a36Sopenharmony_ciSOC_DOUBLE_R_EXT_TLV("DAC1 Playback Volume", 28662306a36Sopenharmony_ci WM8580_DIGITAL_ATTENUATION_DACL1, 28762306a36Sopenharmony_ci WM8580_DIGITAL_ATTENUATION_DACR1, 28862306a36Sopenharmony_ci 0, 0xff, 0, snd_soc_get_volsw, wm8580_out_vu, dac_tlv), 28962306a36Sopenharmony_ciSOC_DOUBLE_R_EXT_TLV("DAC2 Playback Volume", 29062306a36Sopenharmony_ci WM8580_DIGITAL_ATTENUATION_DACL2, 29162306a36Sopenharmony_ci WM8580_DIGITAL_ATTENUATION_DACR2, 29262306a36Sopenharmony_ci 0, 0xff, 0, snd_soc_get_volsw, wm8580_out_vu, dac_tlv), 29362306a36Sopenharmony_ciSOC_DOUBLE_R_EXT_TLV("DAC3 Playback Volume", 29462306a36Sopenharmony_ci WM8580_DIGITAL_ATTENUATION_DACL3, 29562306a36Sopenharmony_ci WM8580_DIGITAL_ATTENUATION_DACR3, 29662306a36Sopenharmony_ci 0, 0xff, 0, snd_soc_get_volsw, wm8580_out_vu, dac_tlv), 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ciSOC_SINGLE("DAC1 Deemphasis Switch", WM8580_DAC_CONTROL3, 0, 1, 0), 29962306a36Sopenharmony_ciSOC_SINGLE("DAC2 Deemphasis Switch", WM8580_DAC_CONTROL3, 1, 1, 0), 30062306a36Sopenharmony_ciSOC_SINGLE("DAC3 Deemphasis Switch", WM8580_DAC_CONTROL3, 2, 1, 0), 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ciSOC_DOUBLE("DAC1 Invert Switch", WM8580_DAC_CONTROL4, 0, 1, 1, 0), 30362306a36Sopenharmony_ciSOC_DOUBLE("DAC2 Invert Switch", WM8580_DAC_CONTROL4, 2, 3, 1, 0), 30462306a36Sopenharmony_ciSOC_DOUBLE("DAC3 Invert Switch", WM8580_DAC_CONTROL4, 4, 5, 1, 0), 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ciSOC_SINGLE("DAC ZC Switch", WM8580_DAC_CONTROL5, 5, 1, 0), 30762306a36Sopenharmony_ciSOC_SINGLE("DAC1 Switch", WM8580_DAC_CONTROL5, 0, 1, 1), 30862306a36Sopenharmony_ciSOC_SINGLE("DAC2 Switch", WM8580_DAC_CONTROL5, 1, 1, 1), 30962306a36Sopenharmony_ciSOC_SINGLE("DAC3 Switch", WM8580_DAC_CONTROL5, 2, 1, 1), 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ciSOC_DOUBLE("Capture Switch", WM8580_ADC_CONTROL1, 0, 1, 1, 1), 31262306a36Sopenharmony_ciSOC_SINGLE("Capture High-Pass Filter Switch", WM8580_ADC_CONTROL1, 4, 1, 0), 31362306a36Sopenharmony_ci}; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_cistatic const struct snd_kcontrol_new wm8581_snd_controls[] = { 31662306a36Sopenharmony_ciSOC_DOUBLE_R_EXT_TLV("DAC4 Playback Volume", 31762306a36Sopenharmony_ci WM8581_DIGITAL_ATTENUATION_DACL4, 31862306a36Sopenharmony_ci WM8581_DIGITAL_ATTENUATION_DACR4, 31962306a36Sopenharmony_ci 0, 0xff, 0, snd_soc_get_volsw, wm8580_out_vu, dac_tlv), 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ciSOC_SINGLE("DAC4 Deemphasis Switch", WM8580_DAC_CONTROL3, 3, 1, 0), 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ciSOC_DOUBLE("DAC4 Invert Switch", WM8580_DAC_CONTROL4, 8, 7, 1, 0), 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ciSOC_SINGLE("DAC4 Switch", WM8580_DAC_CONTROL5, 3, 1, 1), 32662306a36Sopenharmony_ci}; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget wm8580_dapm_widgets[] = { 32962306a36Sopenharmony_ciSND_SOC_DAPM_DAC("DAC1", "Playback", WM8580_PWRDN1, 2, 1), 33062306a36Sopenharmony_ciSND_SOC_DAPM_DAC("DAC2", "Playback", WM8580_PWRDN1, 3, 1), 33162306a36Sopenharmony_ciSND_SOC_DAPM_DAC("DAC3", "Playback", WM8580_PWRDN1, 4, 1), 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("VOUT1L"), 33462306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("VOUT1R"), 33562306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("VOUT2L"), 33662306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("VOUT2R"), 33762306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("VOUT3L"), 33862306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("VOUT3R"), 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ciSND_SOC_DAPM_ADC("ADC", "Capture", WM8580_PWRDN1, 1, 1), 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("AINL"), 34362306a36Sopenharmony_ciSND_SOC_DAPM_INPUT("AINR"), 34462306a36Sopenharmony_ci}; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_cistatic const struct snd_soc_dapm_widget wm8581_dapm_widgets[] = { 34762306a36Sopenharmony_ciSND_SOC_DAPM_DAC("DAC4", "Playback", WM8580_PWRDN1, 5, 1), 34862306a36Sopenharmony_ci 34962306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("VOUT4L"), 35062306a36Sopenharmony_ciSND_SOC_DAPM_OUTPUT("VOUT4R"), 35162306a36Sopenharmony_ci}; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_cistatic const struct snd_soc_dapm_route wm8580_dapm_routes[] = { 35462306a36Sopenharmony_ci { "VOUT1L", NULL, "DAC1" }, 35562306a36Sopenharmony_ci { "VOUT1R", NULL, "DAC1" }, 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci { "VOUT2L", NULL, "DAC2" }, 35862306a36Sopenharmony_ci { "VOUT2R", NULL, "DAC2" }, 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci { "VOUT3L", NULL, "DAC3" }, 36162306a36Sopenharmony_ci { "VOUT3R", NULL, "DAC3" }, 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci { "ADC", NULL, "AINL" }, 36462306a36Sopenharmony_ci { "ADC", NULL, "AINR" }, 36562306a36Sopenharmony_ci}; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic const struct snd_soc_dapm_route wm8581_dapm_routes[] = { 36862306a36Sopenharmony_ci { "VOUT4L", NULL, "DAC4" }, 36962306a36Sopenharmony_ci { "VOUT4R", NULL, "DAC4" }, 37062306a36Sopenharmony_ci}; 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci/* PLL divisors */ 37362306a36Sopenharmony_cistruct _pll_div { 37462306a36Sopenharmony_ci u32 prescale:1; 37562306a36Sopenharmony_ci u32 postscale:1; 37662306a36Sopenharmony_ci u32 freqmode:2; 37762306a36Sopenharmony_ci u32 n:4; 37862306a36Sopenharmony_ci u32 k:24; 37962306a36Sopenharmony_ci}; 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_ci/* The size in bits of the pll divide */ 38262306a36Sopenharmony_ci#define FIXED_PLL_SIZE (1 << 22) 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci/* PLL rate to output rate divisions */ 38562306a36Sopenharmony_cistatic struct { 38662306a36Sopenharmony_ci unsigned int div; 38762306a36Sopenharmony_ci unsigned int freqmode; 38862306a36Sopenharmony_ci unsigned int postscale; 38962306a36Sopenharmony_ci} post_table[] = { 39062306a36Sopenharmony_ci { 2, 0, 0 }, 39162306a36Sopenharmony_ci { 4, 0, 1 }, 39262306a36Sopenharmony_ci { 4, 1, 0 }, 39362306a36Sopenharmony_ci { 8, 1, 1 }, 39462306a36Sopenharmony_ci { 8, 2, 0 }, 39562306a36Sopenharmony_ci { 16, 2, 1 }, 39662306a36Sopenharmony_ci { 12, 3, 0 }, 39762306a36Sopenharmony_ci { 24, 3, 1 } 39862306a36Sopenharmony_ci}; 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic int pll_factors(struct _pll_div *pll_div, unsigned int target, 40162306a36Sopenharmony_ci unsigned int source) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci u64 Kpart; 40462306a36Sopenharmony_ci unsigned int K, Ndiv, Nmod; 40562306a36Sopenharmony_ci int i; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci pr_debug("wm8580: PLL %uHz->%uHz\n", source, target); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci /* Scale the output frequency up; the PLL should run in the 41062306a36Sopenharmony_ci * region of 90-100MHz. 41162306a36Sopenharmony_ci */ 41262306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(post_table); i++) { 41362306a36Sopenharmony_ci if (target * post_table[i].div >= 90000000 && 41462306a36Sopenharmony_ci target * post_table[i].div <= 100000000) { 41562306a36Sopenharmony_ci pll_div->freqmode = post_table[i].freqmode; 41662306a36Sopenharmony_ci pll_div->postscale = post_table[i].postscale; 41762306a36Sopenharmony_ci target *= post_table[i].div; 41862306a36Sopenharmony_ci break; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (i == ARRAY_SIZE(post_table)) { 42362306a36Sopenharmony_ci printk(KERN_ERR "wm8580: Unable to scale output frequency " 42462306a36Sopenharmony_ci "%u\n", target); 42562306a36Sopenharmony_ci return -EINVAL; 42662306a36Sopenharmony_ci } 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci Ndiv = target / source; 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci if (Ndiv < 5) { 43162306a36Sopenharmony_ci source /= 2; 43262306a36Sopenharmony_ci pll_div->prescale = 1; 43362306a36Sopenharmony_ci Ndiv = target / source; 43462306a36Sopenharmony_ci } else 43562306a36Sopenharmony_ci pll_div->prescale = 0; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if ((Ndiv < 5) || (Ndiv > 13)) { 43862306a36Sopenharmony_ci printk(KERN_ERR 43962306a36Sopenharmony_ci "WM8580 N=%u outside supported range\n", Ndiv); 44062306a36Sopenharmony_ci return -EINVAL; 44162306a36Sopenharmony_ci } 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci pll_div->n = Ndiv; 44462306a36Sopenharmony_ci Nmod = target % source; 44562306a36Sopenharmony_ci Kpart = FIXED_PLL_SIZE * (long long)Nmod; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci do_div(Kpart, source); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci K = Kpart & 0xFFFFFFFF; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci pll_div->k = K; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci pr_debug("PLL %x.%x prescale %d freqmode %d postscale %d\n", 45462306a36Sopenharmony_ci pll_div->n, pll_div->k, pll_div->prescale, pll_div->freqmode, 45562306a36Sopenharmony_ci pll_div->postscale); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci return 0; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic int wm8580_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id, 46162306a36Sopenharmony_ci int source, unsigned int freq_in, unsigned int freq_out) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci int offset; 46462306a36Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 46562306a36Sopenharmony_ci struct wm8580_priv *wm8580 = snd_soc_component_get_drvdata(component); 46662306a36Sopenharmony_ci struct pll_state *state; 46762306a36Sopenharmony_ci struct _pll_div pll_div; 46862306a36Sopenharmony_ci unsigned int reg; 46962306a36Sopenharmony_ci unsigned int pwr_mask; 47062306a36Sopenharmony_ci int ret; 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci /* GCC isn't able to work out the ifs below for initialising/using 47362306a36Sopenharmony_ci * pll_div so suppress warnings. 47462306a36Sopenharmony_ci */ 47562306a36Sopenharmony_ci memset(&pll_div, 0, sizeof(pll_div)); 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci switch (pll_id) { 47862306a36Sopenharmony_ci case WM8580_PLLA: 47962306a36Sopenharmony_ci state = &wm8580->a; 48062306a36Sopenharmony_ci offset = 0; 48162306a36Sopenharmony_ci pwr_mask = WM8580_PWRDN2_PLLAPD; 48262306a36Sopenharmony_ci break; 48362306a36Sopenharmony_ci case WM8580_PLLB: 48462306a36Sopenharmony_ci state = &wm8580->b; 48562306a36Sopenharmony_ci offset = 4; 48662306a36Sopenharmony_ci pwr_mask = WM8580_PWRDN2_PLLBPD; 48762306a36Sopenharmony_ci break; 48862306a36Sopenharmony_ci default: 48962306a36Sopenharmony_ci return -ENODEV; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci if (freq_in && freq_out) { 49362306a36Sopenharmony_ci ret = pll_factors(&pll_div, freq_out, freq_in); 49462306a36Sopenharmony_ci if (ret != 0) 49562306a36Sopenharmony_ci return ret; 49662306a36Sopenharmony_ci } 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci state->in = freq_in; 49962306a36Sopenharmony_ci state->out = freq_out; 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci /* Always disable the PLL - it is not safe to leave it running 50262306a36Sopenharmony_ci * while reprogramming it. 50362306a36Sopenharmony_ci */ 50462306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8580_PWRDN2, pwr_mask, pwr_mask); 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (!freq_in || !freq_out) 50762306a36Sopenharmony_ci return 0; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci snd_soc_component_write(component, WM8580_PLLA1 + offset, pll_div.k & 0x1ff); 51062306a36Sopenharmony_ci snd_soc_component_write(component, WM8580_PLLA2 + offset, (pll_div.k >> 9) & 0x1ff); 51162306a36Sopenharmony_ci snd_soc_component_write(component, WM8580_PLLA3 + offset, 51262306a36Sopenharmony_ci (pll_div.k >> 18 & 0xf) | (pll_div.n << 4)); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci reg = snd_soc_component_read(component, WM8580_PLLA4 + offset); 51562306a36Sopenharmony_ci reg &= ~0x1b; 51662306a36Sopenharmony_ci reg |= pll_div.prescale | pll_div.postscale << 1 | 51762306a36Sopenharmony_ci pll_div.freqmode << 3; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci snd_soc_component_write(component, WM8580_PLLA4 + offset, reg); 52062306a36Sopenharmony_ci 52162306a36Sopenharmony_ci /* All done, turn it on */ 52262306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8580_PWRDN2, pwr_mask, 0); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci return 0; 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic const int wm8580_sysclk_ratios[] = { 52862306a36Sopenharmony_ci 128, 192, 256, 384, 512, 768, 1152, 52962306a36Sopenharmony_ci}; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci/* 53262306a36Sopenharmony_ci * Set PCM DAI bit size and sample rate. 53362306a36Sopenharmony_ci */ 53462306a36Sopenharmony_cistatic int wm8580_paif_hw_params(struct snd_pcm_substream *substream, 53562306a36Sopenharmony_ci struct snd_pcm_hw_params *params, 53662306a36Sopenharmony_ci struct snd_soc_dai *dai) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct snd_soc_component *component = dai->component; 53962306a36Sopenharmony_ci struct wm8580_priv *wm8580 = snd_soc_component_get_drvdata(component); 54062306a36Sopenharmony_ci u16 paifa = 0; 54162306a36Sopenharmony_ci u16 paifb = 0; 54262306a36Sopenharmony_ci int i, ratio, osr; 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_ci /* bit size */ 54562306a36Sopenharmony_ci switch (params_width(params)) { 54662306a36Sopenharmony_ci case 16: 54762306a36Sopenharmony_ci paifa |= 0x8; 54862306a36Sopenharmony_ci break; 54962306a36Sopenharmony_ci case 20: 55062306a36Sopenharmony_ci paifa |= 0x0; 55162306a36Sopenharmony_ci paifb |= WM8580_AIF_LENGTH_20; 55262306a36Sopenharmony_ci break; 55362306a36Sopenharmony_ci case 24: 55462306a36Sopenharmony_ci paifa |= 0x0; 55562306a36Sopenharmony_ci paifb |= WM8580_AIF_LENGTH_24; 55662306a36Sopenharmony_ci break; 55762306a36Sopenharmony_ci case 32: 55862306a36Sopenharmony_ci paifa |= 0x0; 55962306a36Sopenharmony_ci paifb |= WM8580_AIF_LENGTH_32; 56062306a36Sopenharmony_ci break; 56162306a36Sopenharmony_ci default: 56262306a36Sopenharmony_ci return -EINVAL; 56362306a36Sopenharmony_ci } 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_ci /* Look up the SYSCLK ratio; accept only exact matches */ 56662306a36Sopenharmony_ci ratio = wm8580->sysclk[dai->driver->id] / params_rate(params); 56762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(wm8580_sysclk_ratios); i++) 56862306a36Sopenharmony_ci if (ratio == wm8580_sysclk_ratios[i]) 56962306a36Sopenharmony_ci break; 57062306a36Sopenharmony_ci if (i == ARRAY_SIZE(wm8580_sysclk_ratios)) { 57162306a36Sopenharmony_ci dev_err(component->dev, "Invalid clock ratio %d/%d\n", 57262306a36Sopenharmony_ci wm8580->sysclk[dai->driver->id], params_rate(params)); 57362306a36Sopenharmony_ci return -EINVAL; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci paifa |= i; 57662306a36Sopenharmony_ci dev_dbg(component->dev, "Running at %dfs with %dHz clock\n", 57762306a36Sopenharmony_ci wm8580_sysclk_ratios[i], wm8580->sysclk[dai->driver->id]); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { 58062306a36Sopenharmony_ci switch (ratio) { 58162306a36Sopenharmony_ci case 128: 58262306a36Sopenharmony_ci case 192: 58362306a36Sopenharmony_ci osr = WM8580_DACOSR; 58462306a36Sopenharmony_ci dev_dbg(component->dev, "Selecting 64x OSR\n"); 58562306a36Sopenharmony_ci break; 58662306a36Sopenharmony_ci default: 58762306a36Sopenharmony_ci osr = 0; 58862306a36Sopenharmony_ci dev_dbg(component->dev, "Selecting 128x OSR\n"); 58962306a36Sopenharmony_ci break; 59062306a36Sopenharmony_ci } 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8580_PAIF3, WM8580_DACOSR, osr); 59362306a36Sopenharmony_ci } 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8580_PAIF1 + dai->driver->id, 59662306a36Sopenharmony_ci WM8580_AIF_RATE_MASK | WM8580_AIF_BCLKSEL_MASK, 59762306a36Sopenharmony_ci paifa); 59862306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8580_PAIF3 + dai->driver->id, 59962306a36Sopenharmony_ci WM8580_AIF_LENGTH_MASK, paifb); 60062306a36Sopenharmony_ci return 0; 60162306a36Sopenharmony_ci} 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_cistatic int wm8580_set_paif_dai_fmt(struct snd_soc_dai *codec_dai, 60462306a36Sopenharmony_ci unsigned int fmt) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 60762306a36Sopenharmony_ci unsigned int aifa; 60862306a36Sopenharmony_ci unsigned int aifb; 60962306a36Sopenharmony_ci int can_invert_lrclk; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci aifa = snd_soc_component_read(component, WM8580_PAIF1 + codec_dai->driver->id); 61262306a36Sopenharmony_ci aifb = snd_soc_component_read(component, WM8580_PAIF3 + codec_dai->driver->id); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci aifb &= ~(WM8580_AIF_FMT_MASK | WM8580_AIF_LRP | WM8580_AIF_BCP); 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { 61762306a36Sopenharmony_ci case SND_SOC_DAIFMT_CBS_CFS: 61862306a36Sopenharmony_ci aifa &= ~WM8580_AIF_MS; 61962306a36Sopenharmony_ci break; 62062306a36Sopenharmony_ci case SND_SOC_DAIFMT_CBM_CFM: 62162306a36Sopenharmony_ci aifa |= WM8580_AIF_MS; 62262306a36Sopenharmony_ci break; 62362306a36Sopenharmony_ci default: 62462306a36Sopenharmony_ci return -EINVAL; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { 62862306a36Sopenharmony_ci case SND_SOC_DAIFMT_I2S: 62962306a36Sopenharmony_ci can_invert_lrclk = 1; 63062306a36Sopenharmony_ci aifb |= WM8580_AIF_FMT_I2S; 63162306a36Sopenharmony_ci break; 63262306a36Sopenharmony_ci case SND_SOC_DAIFMT_RIGHT_J: 63362306a36Sopenharmony_ci can_invert_lrclk = 1; 63462306a36Sopenharmony_ci aifb |= WM8580_AIF_FMT_RIGHTJ; 63562306a36Sopenharmony_ci break; 63662306a36Sopenharmony_ci case SND_SOC_DAIFMT_LEFT_J: 63762306a36Sopenharmony_ci can_invert_lrclk = 1; 63862306a36Sopenharmony_ci aifb |= WM8580_AIF_FMT_LEFTJ; 63962306a36Sopenharmony_ci break; 64062306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_A: 64162306a36Sopenharmony_ci can_invert_lrclk = 0; 64262306a36Sopenharmony_ci aifb |= WM8580_AIF_FMT_DSP; 64362306a36Sopenharmony_ci break; 64462306a36Sopenharmony_ci case SND_SOC_DAIFMT_DSP_B: 64562306a36Sopenharmony_ci can_invert_lrclk = 0; 64662306a36Sopenharmony_ci aifb |= WM8580_AIF_FMT_DSP; 64762306a36Sopenharmony_ci aifb |= WM8580_AIF_LRP; 64862306a36Sopenharmony_ci break; 64962306a36Sopenharmony_ci default: 65062306a36Sopenharmony_ci return -EINVAL; 65162306a36Sopenharmony_ci } 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci switch (fmt & SND_SOC_DAIFMT_INV_MASK) { 65462306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_NF: 65562306a36Sopenharmony_ci break; 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_IF: 65862306a36Sopenharmony_ci if (!can_invert_lrclk) 65962306a36Sopenharmony_ci return -EINVAL; 66062306a36Sopenharmony_ci aifb |= WM8580_AIF_BCP; 66162306a36Sopenharmony_ci aifb |= WM8580_AIF_LRP; 66262306a36Sopenharmony_ci break; 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci case SND_SOC_DAIFMT_IB_NF: 66562306a36Sopenharmony_ci aifb |= WM8580_AIF_BCP; 66662306a36Sopenharmony_ci break; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci case SND_SOC_DAIFMT_NB_IF: 66962306a36Sopenharmony_ci if (!can_invert_lrclk) 67062306a36Sopenharmony_ci return -EINVAL; 67162306a36Sopenharmony_ci aifb |= WM8580_AIF_LRP; 67262306a36Sopenharmony_ci break; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci default: 67562306a36Sopenharmony_ci return -EINVAL; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci snd_soc_component_write(component, WM8580_PAIF1 + codec_dai->driver->id, aifa); 67962306a36Sopenharmony_ci snd_soc_component_write(component, WM8580_PAIF3 + codec_dai->driver->id, aifb); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci return 0; 68262306a36Sopenharmony_ci} 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_cistatic int wm8580_set_dai_clkdiv(struct snd_soc_dai *codec_dai, 68562306a36Sopenharmony_ci int div_id, int div) 68662306a36Sopenharmony_ci{ 68762306a36Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 68862306a36Sopenharmony_ci unsigned int reg; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci switch (div_id) { 69162306a36Sopenharmony_ci case WM8580_MCLK: 69262306a36Sopenharmony_ci reg = snd_soc_component_read(component, WM8580_PLLB4); 69362306a36Sopenharmony_ci reg &= ~WM8580_PLLB4_MCLKOUTSRC_MASK; 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_ci switch (div) { 69662306a36Sopenharmony_ci case WM8580_CLKSRC_MCLK: 69762306a36Sopenharmony_ci /* Input */ 69862306a36Sopenharmony_ci break; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci case WM8580_CLKSRC_PLLA: 70162306a36Sopenharmony_ci reg |= WM8580_PLLB4_MCLKOUTSRC_PLLA; 70262306a36Sopenharmony_ci break; 70362306a36Sopenharmony_ci case WM8580_CLKSRC_PLLB: 70462306a36Sopenharmony_ci reg |= WM8580_PLLB4_MCLKOUTSRC_PLLB; 70562306a36Sopenharmony_ci break; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci case WM8580_CLKSRC_OSC: 70862306a36Sopenharmony_ci reg |= WM8580_PLLB4_MCLKOUTSRC_OSC; 70962306a36Sopenharmony_ci break; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci default: 71262306a36Sopenharmony_ci return -EINVAL; 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci snd_soc_component_write(component, WM8580_PLLB4, reg); 71562306a36Sopenharmony_ci break; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci case WM8580_CLKOUTSRC: 71862306a36Sopenharmony_ci reg = snd_soc_component_read(component, WM8580_PLLB4); 71962306a36Sopenharmony_ci reg &= ~WM8580_PLLB4_CLKOUTSRC_MASK; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci switch (div) { 72262306a36Sopenharmony_ci case WM8580_CLKSRC_NONE: 72362306a36Sopenharmony_ci break; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci case WM8580_CLKSRC_PLLA: 72662306a36Sopenharmony_ci reg |= WM8580_PLLB4_CLKOUTSRC_PLLACLK; 72762306a36Sopenharmony_ci break; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci case WM8580_CLKSRC_PLLB: 73062306a36Sopenharmony_ci reg |= WM8580_PLLB4_CLKOUTSRC_PLLBCLK; 73162306a36Sopenharmony_ci break; 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci case WM8580_CLKSRC_OSC: 73462306a36Sopenharmony_ci reg |= WM8580_PLLB4_CLKOUTSRC_OSCCLK; 73562306a36Sopenharmony_ci break; 73662306a36Sopenharmony_ci 73762306a36Sopenharmony_ci default: 73862306a36Sopenharmony_ci return -EINVAL; 73962306a36Sopenharmony_ci } 74062306a36Sopenharmony_ci snd_soc_component_write(component, WM8580_PLLB4, reg); 74162306a36Sopenharmony_ci break; 74262306a36Sopenharmony_ci 74362306a36Sopenharmony_ci default: 74462306a36Sopenharmony_ci return -EINVAL; 74562306a36Sopenharmony_ci } 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci return 0; 74862306a36Sopenharmony_ci} 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_cistatic int wm8580_set_sysclk(struct snd_soc_dai *dai, int clk_id, 75162306a36Sopenharmony_ci unsigned int freq, int dir) 75262306a36Sopenharmony_ci{ 75362306a36Sopenharmony_ci struct snd_soc_component *component = dai->component; 75462306a36Sopenharmony_ci struct wm8580_priv *wm8580 = snd_soc_component_get_drvdata(component); 75562306a36Sopenharmony_ci int ret, sel, sel_mask, sel_shift; 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci switch (dai->driver->id) { 75862306a36Sopenharmony_ci case WM8580_DAI_PAIFRX: 75962306a36Sopenharmony_ci sel_mask = 0x3; 76062306a36Sopenharmony_ci sel_shift = 0; 76162306a36Sopenharmony_ci break; 76262306a36Sopenharmony_ci 76362306a36Sopenharmony_ci case WM8580_DAI_PAIFTX: 76462306a36Sopenharmony_ci sel_mask = 0xc; 76562306a36Sopenharmony_ci sel_shift = 2; 76662306a36Sopenharmony_ci break; 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci default: 76962306a36Sopenharmony_ci WARN(1, "Unknown DAI driver ID\n"); 77062306a36Sopenharmony_ci return -EINVAL; 77162306a36Sopenharmony_ci } 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci switch (clk_id) { 77462306a36Sopenharmony_ci case WM8580_CLKSRC_ADCMCLK: 77562306a36Sopenharmony_ci if (dai->driver->id != WM8580_DAI_PAIFTX) 77662306a36Sopenharmony_ci return -EINVAL; 77762306a36Sopenharmony_ci sel = 0 << sel_shift; 77862306a36Sopenharmony_ci break; 77962306a36Sopenharmony_ci case WM8580_CLKSRC_PLLA: 78062306a36Sopenharmony_ci sel = 1 << sel_shift; 78162306a36Sopenharmony_ci break; 78262306a36Sopenharmony_ci case WM8580_CLKSRC_PLLB: 78362306a36Sopenharmony_ci sel = 2 << sel_shift; 78462306a36Sopenharmony_ci break; 78562306a36Sopenharmony_ci case WM8580_CLKSRC_MCLK: 78662306a36Sopenharmony_ci sel = 3 << sel_shift; 78762306a36Sopenharmony_ci break; 78862306a36Sopenharmony_ci default: 78962306a36Sopenharmony_ci dev_err(component->dev, "Unknown clock %d\n", clk_id); 79062306a36Sopenharmony_ci return -EINVAL; 79162306a36Sopenharmony_ci } 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci /* We really should validate PLL settings but not yet */ 79462306a36Sopenharmony_ci wm8580->sysclk[dai->driver->id] = freq; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci ret = snd_soc_component_update_bits(component, WM8580_CLKSEL, sel_mask, sel); 79762306a36Sopenharmony_ci if (ret < 0) 79862306a36Sopenharmony_ci return ret; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci return 0; 80162306a36Sopenharmony_ci} 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_cistatic int wm8580_mute(struct snd_soc_dai *codec_dai, int mute, int direction) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci struct snd_soc_component *component = codec_dai->component; 80662306a36Sopenharmony_ci unsigned int reg; 80762306a36Sopenharmony_ci 80862306a36Sopenharmony_ci reg = snd_soc_component_read(component, WM8580_DAC_CONTROL5); 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci if (mute) 81162306a36Sopenharmony_ci reg |= WM8580_DAC_CONTROL5_MUTEALL; 81262306a36Sopenharmony_ci else 81362306a36Sopenharmony_ci reg &= ~WM8580_DAC_CONTROL5_MUTEALL; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci snd_soc_component_write(component, WM8580_DAC_CONTROL5, reg); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci return 0; 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cistatic int wm8580_set_bias_level(struct snd_soc_component *component, 82162306a36Sopenharmony_ci enum snd_soc_bias_level level) 82262306a36Sopenharmony_ci{ 82362306a36Sopenharmony_ci switch (level) { 82462306a36Sopenharmony_ci case SND_SOC_BIAS_ON: 82562306a36Sopenharmony_ci case SND_SOC_BIAS_PREPARE: 82662306a36Sopenharmony_ci break; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci case SND_SOC_BIAS_STANDBY: 82962306a36Sopenharmony_ci if (snd_soc_component_get_bias_level(component) == SND_SOC_BIAS_OFF) { 83062306a36Sopenharmony_ci /* Power up and get individual control of the DACs */ 83162306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8580_PWRDN1, 83262306a36Sopenharmony_ci WM8580_PWRDN1_PWDN | 83362306a36Sopenharmony_ci WM8580_PWRDN1_ALLDACPD, 0); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci /* Make VMID high impedance */ 83662306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8580_ADC_CONTROL1, 83762306a36Sopenharmony_ci 0x100, 0); 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci break; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci case SND_SOC_BIAS_OFF: 84262306a36Sopenharmony_ci snd_soc_component_update_bits(component, WM8580_PWRDN1, 84362306a36Sopenharmony_ci WM8580_PWRDN1_PWDN, WM8580_PWRDN1_PWDN); 84462306a36Sopenharmony_ci break; 84562306a36Sopenharmony_ci } 84662306a36Sopenharmony_ci return 0; 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_cistatic int wm8580_playback_startup(struct snd_pcm_substream *substream, 85062306a36Sopenharmony_ci struct snd_soc_dai *dai) 85162306a36Sopenharmony_ci{ 85262306a36Sopenharmony_ci struct snd_soc_component *component = dai->component; 85362306a36Sopenharmony_ci struct wm8580_priv *wm8580 = snd_soc_component_get_drvdata(component); 85462306a36Sopenharmony_ci 85562306a36Sopenharmony_ci return snd_pcm_hw_constraint_minmax(substream->runtime, 85662306a36Sopenharmony_ci SNDRV_PCM_HW_PARAM_CHANNELS, 1, wm8580->drvdata->num_dacs * 2); 85762306a36Sopenharmony_ci} 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci#define WM8580_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\ 86062306a36Sopenharmony_ci SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic const struct snd_soc_dai_ops wm8580_dai_ops_playback = { 86362306a36Sopenharmony_ci .startup = wm8580_playback_startup, 86462306a36Sopenharmony_ci .set_sysclk = wm8580_set_sysclk, 86562306a36Sopenharmony_ci .hw_params = wm8580_paif_hw_params, 86662306a36Sopenharmony_ci .set_fmt = wm8580_set_paif_dai_fmt, 86762306a36Sopenharmony_ci .set_clkdiv = wm8580_set_dai_clkdiv, 86862306a36Sopenharmony_ci .set_pll = wm8580_set_dai_pll, 86962306a36Sopenharmony_ci .mute_stream = wm8580_mute, 87062306a36Sopenharmony_ci .no_capture_mute = 1, 87162306a36Sopenharmony_ci}; 87262306a36Sopenharmony_ci 87362306a36Sopenharmony_cistatic const struct snd_soc_dai_ops wm8580_dai_ops_capture = { 87462306a36Sopenharmony_ci .set_sysclk = wm8580_set_sysclk, 87562306a36Sopenharmony_ci .hw_params = wm8580_paif_hw_params, 87662306a36Sopenharmony_ci .set_fmt = wm8580_set_paif_dai_fmt, 87762306a36Sopenharmony_ci .set_clkdiv = wm8580_set_dai_clkdiv, 87862306a36Sopenharmony_ci .set_pll = wm8580_set_dai_pll, 87962306a36Sopenharmony_ci}; 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cistatic struct snd_soc_dai_driver wm8580_dai[] = { 88262306a36Sopenharmony_ci { 88362306a36Sopenharmony_ci .name = "wm8580-hifi-playback", 88462306a36Sopenharmony_ci .id = WM8580_DAI_PAIFRX, 88562306a36Sopenharmony_ci .playback = { 88662306a36Sopenharmony_ci .stream_name = "Playback", 88762306a36Sopenharmony_ci .channels_min = 1, 88862306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000, 88962306a36Sopenharmony_ci .formats = WM8580_FORMATS, 89062306a36Sopenharmony_ci }, 89162306a36Sopenharmony_ci .ops = &wm8580_dai_ops_playback, 89262306a36Sopenharmony_ci }, 89362306a36Sopenharmony_ci { 89462306a36Sopenharmony_ci .name = "wm8580-hifi-capture", 89562306a36Sopenharmony_ci .id = WM8580_DAI_PAIFTX, 89662306a36Sopenharmony_ci .capture = { 89762306a36Sopenharmony_ci .stream_name = "Capture", 89862306a36Sopenharmony_ci .channels_min = 2, 89962306a36Sopenharmony_ci .channels_max = 2, 90062306a36Sopenharmony_ci .rates = SNDRV_PCM_RATE_8000_192000, 90162306a36Sopenharmony_ci .formats = WM8580_FORMATS, 90262306a36Sopenharmony_ci }, 90362306a36Sopenharmony_ci .ops = &wm8580_dai_ops_capture, 90462306a36Sopenharmony_ci }, 90562306a36Sopenharmony_ci}; 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_cistatic int wm8580_probe(struct snd_soc_component *component) 90862306a36Sopenharmony_ci{ 90962306a36Sopenharmony_ci struct wm8580_priv *wm8580 = snd_soc_component_get_drvdata(component); 91062306a36Sopenharmony_ci struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); 91162306a36Sopenharmony_ci int ret = 0; 91262306a36Sopenharmony_ci 91362306a36Sopenharmony_ci switch (wm8580->drvdata->num_dacs) { 91462306a36Sopenharmony_ci case 4: 91562306a36Sopenharmony_ci snd_soc_add_component_controls(component, wm8581_snd_controls, 91662306a36Sopenharmony_ci ARRAY_SIZE(wm8581_snd_controls)); 91762306a36Sopenharmony_ci snd_soc_dapm_new_controls(dapm, wm8581_dapm_widgets, 91862306a36Sopenharmony_ci ARRAY_SIZE(wm8581_dapm_widgets)); 91962306a36Sopenharmony_ci snd_soc_dapm_add_routes(dapm, wm8581_dapm_routes, 92062306a36Sopenharmony_ci ARRAY_SIZE(wm8581_dapm_routes)); 92162306a36Sopenharmony_ci break; 92262306a36Sopenharmony_ci default: 92362306a36Sopenharmony_ci break; 92462306a36Sopenharmony_ci } 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci ret = regulator_bulk_enable(ARRAY_SIZE(wm8580->supplies), 92762306a36Sopenharmony_ci wm8580->supplies); 92862306a36Sopenharmony_ci if (ret != 0) { 92962306a36Sopenharmony_ci dev_err(component->dev, "Failed to enable supplies: %d\n", ret); 93062306a36Sopenharmony_ci goto err_regulator_get; 93162306a36Sopenharmony_ci } 93262306a36Sopenharmony_ci 93362306a36Sopenharmony_ci /* Get the codec into a known state */ 93462306a36Sopenharmony_ci ret = snd_soc_component_write(component, WM8580_RESET, 0); 93562306a36Sopenharmony_ci if (ret != 0) { 93662306a36Sopenharmony_ci dev_err(component->dev, "Failed to reset component: %d\n", ret); 93762306a36Sopenharmony_ci goto err_regulator_enable; 93862306a36Sopenharmony_ci } 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_ci return 0; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_cierr_regulator_enable: 94362306a36Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies); 94462306a36Sopenharmony_cierr_regulator_get: 94562306a36Sopenharmony_ci return ret; 94662306a36Sopenharmony_ci} 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci/* power down chip */ 94962306a36Sopenharmony_cistatic void wm8580_remove(struct snd_soc_component *component) 95062306a36Sopenharmony_ci{ 95162306a36Sopenharmony_ci struct wm8580_priv *wm8580 = snd_soc_component_get_drvdata(component); 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies); 95462306a36Sopenharmony_ci} 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_cistatic const struct snd_soc_component_driver soc_component_dev_wm8580 = { 95762306a36Sopenharmony_ci .probe = wm8580_probe, 95862306a36Sopenharmony_ci .remove = wm8580_remove, 95962306a36Sopenharmony_ci .set_bias_level = wm8580_set_bias_level, 96062306a36Sopenharmony_ci .controls = wm8580_snd_controls, 96162306a36Sopenharmony_ci .num_controls = ARRAY_SIZE(wm8580_snd_controls), 96262306a36Sopenharmony_ci .dapm_widgets = wm8580_dapm_widgets, 96362306a36Sopenharmony_ci .num_dapm_widgets = ARRAY_SIZE(wm8580_dapm_widgets), 96462306a36Sopenharmony_ci .dapm_routes = wm8580_dapm_routes, 96562306a36Sopenharmony_ci .num_dapm_routes = ARRAY_SIZE(wm8580_dapm_routes), 96662306a36Sopenharmony_ci .idle_bias_on = 1, 96762306a36Sopenharmony_ci .use_pmdown_time = 1, 96862306a36Sopenharmony_ci .endianness = 1, 96962306a36Sopenharmony_ci}; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_cistatic const struct regmap_config wm8580_regmap = { 97262306a36Sopenharmony_ci .reg_bits = 7, 97362306a36Sopenharmony_ci .val_bits = 9, 97462306a36Sopenharmony_ci .max_register = WM8580_MAX_REGISTER, 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci .reg_defaults = wm8580_reg_defaults, 97762306a36Sopenharmony_ci .num_reg_defaults = ARRAY_SIZE(wm8580_reg_defaults), 97862306a36Sopenharmony_ci .cache_type = REGCACHE_MAPLE, 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci .volatile_reg = wm8580_volatile, 98162306a36Sopenharmony_ci}; 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_cistatic const struct wm8580_driver_data wm8580_data = { 98462306a36Sopenharmony_ci .num_dacs = 3, 98562306a36Sopenharmony_ci}; 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_cistatic const struct wm8580_driver_data wm8581_data = { 98862306a36Sopenharmony_ci .num_dacs = 4, 98962306a36Sopenharmony_ci}; 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_cistatic const struct of_device_id wm8580_of_match[] = { 99262306a36Sopenharmony_ci { .compatible = "wlf,wm8580", .data = &wm8580_data }, 99362306a36Sopenharmony_ci { .compatible = "wlf,wm8581", .data = &wm8581_data }, 99462306a36Sopenharmony_ci { }, 99562306a36Sopenharmony_ci}; 99662306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, wm8580_of_match); 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_cistatic int wm8580_i2c_probe(struct i2c_client *i2c) 99962306a36Sopenharmony_ci{ 100062306a36Sopenharmony_ci const struct of_device_id *of_id; 100162306a36Sopenharmony_ci struct wm8580_priv *wm8580; 100262306a36Sopenharmony_ci int ret, i; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci wm8580 = devm_kzalloc(&i2c->dev, sizeof(struct wm8580_priv), 100562306a36Sopenharmony_ci GFP_KERNEL); 100662306a36Sopenharmony_ci if (wm8580 == NULL) 100762306a36Sopenharmony_ci return -ENOMEM; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci wm8580->regmap = devm_regmap_init_i2c(i2c, &wm8580_regmap); 101062306a36Sopenharmony_ci if (IS_ERR(wm8580->regmap)) 101162306a36Sopenharmony_ci return PTR_ERR(wm8580->regmap); 101262306a36Sopenharmony_ci 101362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(wm8580->supplies); i++) 101462306a36Sopenharmony_ci wm8580->supplies[i].supply = wm8580_supply_names[i]; 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_ci ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8580->supplies), 101762306a36Sopenharmony_ci wm8580->supplies); 101862306a36Sopenharmony_ci if (ret != 0) { 101962306a36Sopenharmony_ci dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret); 102062306a36Sopenharmony_ci return ret; 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci i2c_set_clientdata(i2c, wm8580); 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci of_id = of_match_device(wm8580_of_match, &i2c->dev); 102662306a36Sopenharmony_ci if (of_id) 102762306a36Sopenharmony_ci wm8580->drvdata = of_id->data; 102862306a36Sopenharmony_ci 102962306a36Sopenharmony_ci if (!wm8580->drvdata) { 103062306a36Sopenharmony_ci dev_err(&i2c->dev, "failed to find driver data\n"); 103162306a36Sopenharmony_ci return -EINVAL; 103262306a36Sopenharmony_ci } 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci ret = devm_snd_soc_register_component(&i2c->dev, 103562306a36Sopenharmony_ci &soc_component_dev_wm8580, wm8580_dai, ARRAY_SIZE(wm8580_dai)); 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci return ret; 103862306a36Sopenharmony_ci} 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_cistatic const struct i2c_device_id wm8580_i2c_id[] = { 104162306a36Sopenharmony_ci { "wm8580", (kernel_ulong_t)&wm8580_data }, 104262306a36Sopenharmony_ci { "wm8581", (kernel_ulong_t)&wm8581_data }, 104362306a36Sopenharmony_ci { } 104462306a36Sopenharmony_ci}; 104562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, wm8580_i2c_id); 104662306a36Sopenharmony_ci 104762306a36Sopenharmony_cistatic struct i2c_driver wm8580_i2c_driver = { 104862306a36Sopenharmony_ci .driver = { 104962306a36Sopenharmony_ci .name = "wm8580", 105062306a36Sopenharmony_ci .of_match_table = wm8580_of_match, 105162306a36Sopenharmony_ci }, 105262306a36Sopenharmony_ci .probe = wm8580_i2c_probe, 105362306a36Sopenharmony_ci .id_table = wm8580_i2c_id, 105462306a36Sopenharmony_ci}; 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_cimodule_i2c_driver(wm8580_i2c_driver); 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ciMODULE_DESCRIPTION("ASoC WM8580 driver"); 105962306a36Sopenharmony_ciMODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 106062306a36Sopenharmony_ciMODULE_AUTHOR("Matt Flax <flatmax@flatmax.org>"); 106162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 1062