| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 1 | /* | 
|  | 2 | * CS4270 ALSA SoC (ASoC) codec driver | 
|  | 3 | * | 
|  | 4 | * Author: Timur Tabi <timur@freescale.com> | 
|  | 5 | * | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 6 | * Copyright 2007-2009 Freescale Semiconductor, Inc.  This file is licensed | 
|  | 7 | * under the terms of the GNU General Public License version 2.  This | 
|  | 8 | * program is licensed "as is" without any warranty of any kind, whether | 
|  | 9 | * express or implied. | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 10 | * | 
|  | 11 | * This is an ASoC device driver for the Cirrus Logic CS4270 codec. | 
|  | 12 | * | 
|  | 13 | * Current features/limitations: | 
|  | 14 | * | 
| Daniel Mack | b191f63 | 2009-03-08 17:51:52 +0100 | [diff] [blame] | 15 | * - Software mode is supported.  Stand-alone mode is not supported. | 
|  | 16 | * - Only I2C is supported, not SPI | 
|  | 17 | * - Support for master and slave mode | 
|  | 18 | * - The machine driver's 'startup' function must call | 
|  | 19 | *   cs4270_set_dai_sysclk() with the value of MCLK. | 
|  | 20 | * - Only I2S and left-justified modes are supported | 
| Daniel Mack | 5e7c034 | 2009-05-06 01:26:01 +0200 | [diff] [blame] | 21 | * - Power management is supported | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 22 | */ | 
|  | 23 |  | 
|  | 24 | #include <linux/module.h> | 
|  | 25 | #include <linux/platform_device.h> | 
| Tejun Heo | 5a0e3ad | 2010-03-24 17:04:11 +0900 | [diff] [blame] | 26 | #include <linux/slab.h> | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 27 | #include <sound/core.h> | 
|  | 28 | #include <sound/soc.h> | 
|  | 29 | #include <sound/initval.h> | 
|  | 30 | #include <linux/i2c.h> | 
| Daniel Mack | 5e7c034 | 2009-05-06 01:26:01 +0200 | [diff] [blame] | 31 | #include <linux/delay.h> | 
| Daniel Mack | ffbfd33 | 2009-11-30 17:56:11 +0100 | [diff] [blame] | 32 | #include <linux/regulator/consumer.h> | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 33 |  | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 34 | /* | 
|  | 35 | * The codec isn't really big-endian or little-endian, since the I2S | 
|  | 36 | * interface requires data to be sent serially with the MSbit first. | 
|  | 37 | * However, to support BE and LE I2S devices, we specify both here.  That | 
|  | 38 | * way, ALSA will always match the bit patterns. | 
|  | 39 | */ | 
|  | 40 | #define CS4270_FORMATS (SNDRV_PCM_FMTBIT_S8      | \ | 
|  | 41 | SNDRV_PCM_FMTBIT_S16_LE  | SNDRV_PCM_FMTBIT_S16_BE  | \ | 
|  | 42 | SNDRV_PCM_FMTBIT_S18_3LE | SNDRV_PCM_FMTBIT_S18_3BE | \ | 
|  | 43 | SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S20_3BE | \ | 
|  | 44 | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S24_3BE | \ | 
|  | 45 | SNDRV_PCM_FMTBIT_S24_LE  | SNDRV_PCM_FMTBIT_S24_BE) | 
|  | 46 |  | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 47 | /* CS4270 registers addresses */ | 
|  | 48 | #define CS4270_CHIPID	0x01	/* Chip ID */ | 
|  | 49 | #define CS4270_PWRCTL	0x02	/* Power Control */ | 
|  | 50 | #define CS4270_MODE	0x03	/* Mode Control */ | 
|  | 51 | #define CS4270_FORMAT	0x04	/* Serial Format, ADC/DAC Control */ | 
|  | 52 | #define CS4270_TRANS	0x05	/* Transition Control */ | 
|  | 53 | #define CS4270_MUTE	0x06	/* Mute Control */ | 
|  | 54 | #define CS4270_VOLA	0x07	/* DAC Channel A Volume Control */ | 
|  | 55 | #define CS4270_VOLB	0x08	/* DAC Channel B Volume Control */ | 
|  | 56 |  | 
|  | 57 | #define CS4270_FIRSTREG	0x01 | 
|  | 58 | #define CS4270_LASTREG	0x08 | 
|  | 59 | #define CS4270_NUMREGS	(CS4270_LASTREG - CS4270_FIRSTREG + 1) | 
| Daniel Mack | 80ab881 | 2009-05-05 11:25:00 +0200 | [diff] [blame] | 60 | #define CS4270_I2C_INCR	0x80 | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 61 |  | 
|  | 62 | /* Bit masks for the CS4270 registers */ | 
|  | 63 | #define CS4270_CHIPID_ID	0xF0 | 
|  | 64 | #define CS4270_CHIPID_REV	0x0F | 
|  | 65 | #define CS4270_PWRCTL_FREEZE	0x80 | 
|  | 66 | #define CS4270_PWRCTL_PDN_ADC	0x20 | 
|  | 67 | #define CS4270_PWRCTL_PDN_DAC	0x02 | 
|  | 68 | #define CS4270_PWRCTL_PDN	0x01 | 
| Daniel Mack | 5e7c034 | 2009-05-06 01:26:01 +0200 | [diff] [blame] | 69 | #define CS4270_PWRCTL_PDN_ALL	\ | 
|  | 70 | (CS4270_PWRCTL_PDN_ADC | CS4270_PWRCTL_PDN_DAC | CS4270_PWRCTL_PDN) | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 71 | #define CS4270_MODE_SPEED_MASK	0x30 | 
|  | 72 | #define CS4270_MODE_1X		0x00 | 
|  | 73 | #define CS4270_MODE_2X		0x10 | 
|  | 74 | #define CS4270_MODE_4X		0x20 | 
|  | 75 | #define CS4270_MODE_SLAVE	0x30 | 
|  | 76 | #define CS4270_MODE_DIV_MASK	0x0E | 
|  | 77 | #define CS4270_MODE_DIV1	0x00 | 
|  | 78 | #define CS4270_MODE_DIV15	0x02 | 
|  | 79 | #define CS4270_MODE_DIV2	0x04 | 
|  | 80 | #define CS4270_MODE_DIV3	0x06 | 
|  | 81 | #define CS4270_MODE_DIV4	0x08 | 
|  | 82 | #define CS4270_MODE_POPGUARD	0x01 | 
|  | 83 | #define CS4270_FORMAT_FREEZE_A	0x80 | 
|  | 84 | #define CS4270_FORMAT_FREEZE_B	0x40 | 
|  | 85 | #define CS4270_FORMAT_LOOPBACK	0x20 | 
|  | 86 | #define CS4270_FORMAT_DAC_MASK	0x18 | 
|  | 87 | #define CS4270_FORMAT_DAC_LJ	0x00 | 
|  | 88 | #define CS4270_FORMAT_DAC_I2S	0x08 | 
|  | 89 | #define CS4270_FORMAT_DAC_RJ16	0x18 | 
|  | 90 | #define CS4270_FORMAT_DAC_RJ24	0x10 | 
|  | 91 | #define CS4270_FORMAT_ADC_MASK	0x01 | 
|  | 92 | #define CS4270_FORMAT_ADC_LJ	0x00 | 
|  | 93 | #define CS4270_FORMAT_ADC_I2S	0x01 | 
|  | 94 | #define CS4270_TRANS_ONE_VOL	0x80 | 
|  | 95 | #define CS4270_TRANS_SOFT	0x40 | 
|  | 96 | #define CS4270_TRANS_ZERO	0x20 | 
|  | 97 | #define CS4270_TRANS_INV_ADC_A	0x08 | 
|  | 98 | #define CS4270_TRANS_INV_ADC_B	0x10 | 
|  | 99 | #define CS4270_TRANS_INV_DAC_A	0x02 | 
|  | 100 | #define CS4270_TRANS_INV_DAC_B	0x04 | 
|  | 101 | #define CS4270_TRANS_DEEMPH	0x01 | 
|  | 102 | #define CS4270_MUTE_AUTO	0x20 | 
|  | 103 | #define CS4270_MUTE_ADC_A	0x08 | 
|  | 104 | #define CS4270_MUTE_ADC_B	0x10 | 
|  | 105 | #define CS4270_MUTE_POLARITY	0x04 | 
|  | 106 | #define CS4270_MUTE_DAC_A	0x01 | 
|  | 107 | #define CS4270_MUTE_DAC_B	0x02 | 
|  | 108 |  | 
| Timur Tabi | 11b8fca | 2011-01-10 13:28:32 -0600 | [diff] [blame] | 109 | /* Power-on default values for the registers | 
|  | 110 | * | 
|  | 111 | * This array contains the power-on default values of the registers, with the | 
|  | 112 | * exception of the "CHIPID" register (01h).  The lower four bits of that | 
|  | 113 | * register contain the hardware revision, so it is treated as volatile. | 
|  | 114 | * | 
|  | 115 | * Also note that on the CS4270, the first readable register is 1, but ASoC | 
|  | 116 | * assumes the first register is 0.  Therfore, the array must have an entry for | 
|  | 117 | * register 0, but we use cs4270_reg_is_readable() to tell ASoC that it can't | 
|  | 118 | * be read. | 
|  | 119 | */ | 
|  | 120 | static const u8 cs4270_default_reg_cache[CS4270_LASTREG + 1] = { | 
|  | 121 | 0x00, 0x00, 0x00, 0x30, 0x00, 0x60, 0x20, 0x00, 0x00 | 
|  | 122 | }; | 
|  | 123 |  | 
| Daniel Mack | ffbfd33 | 2009-11-30 17:56:11 +0100 | [diff] [blame] | 124 | static const char *supply_names[] = { | 
|  | 125 | "va", "vd", "vlc" | 
|  | 126 | }; | 
|  | 127 |  | 
| Timur Tabi | 0db4d07 | 2009-01-23 16:31:19 -0600 | [diff] [blame] | 128 | /* Private data for the CS4270 */ | 
|  | 129 | struct cs4270_private { | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 130 | enum snd_soc_control_type control_type; | 
|  | 131 | void *control_data; | 
| Timur Tabi | 0db4d07 | 2009-01-23 16:31:19 -0600 | [diff] [blame] | 132 | unsigned int mclk; /* Input frequency of the MCLK pin */ | 
|  | 133 | unsigned int mode; /* The mode (I2S or left-justified) */ | 
| Daniel Mack | 4eae080 | 2009-02-25 14:37:21 +0100 | [diff] [blame] | 134 | unsigned int slave_mode; | 
| Daniel Mack | 1a4ba05 | 2009-04-24 16:37:45 +0200 | [diff] [blame] | 135 | unsigned int manual_mute; | 
| Daniel Mack | ffbfd33 | 2009-11-30 17:56:11 +0100 | [diff] [blame] | 136 |  | 
|  | 137 | /* power domain regulators */ | 
|  | 138 | struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; | 
| Timur Tabi | 0db4d07 | 2009-01-23 16:31:19 -0600 | [diff] [blame] | 139 | }; | 
|  | 140 |  | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 141 | /** | 
|  | 142 | * struct cs4270_mode_ratios - clock ratio tables | 
|  | 143 | * @ratio: the ratio of MCLK to the sample rate | 
|  | 144 | * @speed_mode: the Speed Mode bits to set in the Mode Control register for | 
|  | 145 | *              this ratio | 
|  | 146 | * @mclk: the Ratio Select bits to set in the Mode Control register for this | 
|  | 147 | *        ratio | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 148 | * | 
|  | 149 | * The data for this chart is taken from Table 5 of the CS4270 reference | 
|  | 150 | * manual. | 
|  | 151 | * | 
|  | 152 | * This table is used to determine how to program the Mode Control register. | 
|  | 153 | * It is also used by cs4270_set_dai_sysclk() to tell ALSA which sampling | 
|  | 154 | * rates the CS4270 currently supports. | 
|  | 155 | * | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 156 | * @speed_mode is the corresponding bit pattern to be written to the | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 157 | * MODE bits of the Mode Control Register | 
|  | 158 | * | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 159 | * @mclk is the corresponding bit pattern to be wirten to the MCLK bits of | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 160 | * the Mode Control Register. | 
|  | 161 | * | 
|  | 162 | * In situations where a single ratio is represented by multiple speed | 
|  | 163 | * modes, we favor the slowest speed.  E.g, for a ratio of 128, we pick | 
|  | 164 | * double-speed instead of quad-speed.  However, the CS4270 errata states | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 165 | * that divide-By-1.5 can cause failures, so we avoid that mode where | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 166 | * possible. | 
|  | 167 | * | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 168 | * Errata: There is an errata for the CS4270 where divide-by-1.5 does not | 
|  | 169 | * work if Vd is 3.3V.  If this effects you, select the | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 170 | * CONFIG_SND_SOC_CS4270_VD33_ERRATA Kconfig option, and the driver will | 
|  | 171 | * never select any sample rates that require divide-by-1.5. | 
|  | 172 | */ | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 173 | struct cs4270_mode_ratios { | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 174 | unsigned int ratio; | 
|  | 175 | u8 speed_mode; | 
|  | 176 | u8 mclk; | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 177 | }; | 
|  | 178 |  | 
| Timur Tabi | d9fb7fb | 2009-02-02 14:50:45 -0600 | [diff] [blame] | 179 | static struct cs4270_mode_ratios cs4270_mode_ratios[] = { | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 180 | {64, CS4270_MODE_4X, CS4270_MODE_DIV1}, | 
|  | 181 | #ifndef CONFIG_SND_SOC_CS4270_VD33_ERRATA | 
|  | 182 | {96, CS4270_MODE_4X, CS4270_MODE_DIV15}, | 
|  | 183 | #endif | 
|  | 184 | {128, CS4270_MODE_2X, CS4270_MODE_DIV1}, | 
|  | 185 | {192, CS4270_MODE_4X, CS4270_MODE_DIV3}, | 
|  | 186 | {256, CS4270_MODE_1X, CS4270_MODE_DIV1}, | 
|  | 187 | {384, CS4270_MODE_2X, CS4270_MODE_DIV3}, | 
|  | 188 | {512, CS4270_MODE_1X, CS4270_MODE_DIV2}, | 
|  | 189 | {768, CS4270_MODE_1X, CS4270_MODE_DIV3}, | 
|  | 190 | {1024, CS4270_MODE_1X, CS4270_MODE_DIV4} | 
|  | 191 | }; | 
|  | 192 |  | 
|  | 193 | /* The number of MCLK/LRCK ratios supported by the CS4270 */ | 
|  | 194 | #define NUM_MCLK_RATIOS		ARRAY_SIZE(cs4270_mode_ratios) | 
|  | 195 |  | 
| Dimitris Papastamos | d4754ec | 2011-01-13 12:20:37 +0000 | [diff] [blame] | 196 | static int cs4270_reg_is_readable(struct snd_soc_codec *codec, unsigned int reg) | 
| Timur Tabi | 11b8fca | 2011-01-10 13:28:32 -0600 | [diff] [blame] | 197 | { | 
|  | 198 | return (reg >= CS4270_FIRSTREG) && (reg <= CS4270_LASTREG); | 
|  | 199 | } | 
|  | 200 |  | 
| Dimitris Papastamos | d4754ec | 2011-01-13 12:20:37 +0000 | [diff] [blame] | 201 | static int cs4270_reg_is_volatile(struct snd_soc_codec *codec, unsigned int reg) | 
| Timur Tabi | 11b8fca | 2011-01-10 13:28:32 -0600 | [diff] [blame] | 202 | { | 
|  | 203 | /* Unreadable registers are considered volatile */ | 
|  | 204 | if ((reg < CS4270_FIRSTREG) || (reg > CS4270_LASTREG)) | 
|  | 205 | return 1; | 
|  | 206 |  | 
|  | 207 | return reg == CS4270_CHIPID; | 
|  | 208 | } | 
|  | 209 |  | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 210 | /** | 
|  | 211 | * cs4270_set_dai_sysclk - determine the CS4270 samples rates. | 
|  | 212 | * @codec_dai: the codec DAI | 
|  | 213 | * @clk_id: the clock ID (ignored) | 
|  | 214 | * @freq: the MCLK input frequency | 
|  | 215 | * @dir: the clock direction (ignored) | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 216 | * | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 217 | * This function is used to tell the codec driver what the input MCLK | 
|  | 218 | * frequency is. | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 219 | * | 
|  | 220 | * The value of MCLK is used to determine which sample rates are supported | 
|  | 221 | * by the CS4270.  The ratio of MCLK / Fs must be equal to one of nine | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 222 | * supported values - 64, 96, 128, 192, 256, 384, 512, 768, and 1024. | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 223 | * | 
|  | 224 | * This function calculates the nine ratios and determines which ones match | 
|  | 225 | * a standard sample rate.  If there's a match, then it is added to the list | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 226 | * of supported sample rates. | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 227 | * | 
|  | 228 | * This function must be called by the machine driver's 'startup' function, | 
|  | 229 | * otherwise the list of supported sample rates will not be available in | 
|  | 230 | * time for ALSA. | 
| Daniel Mack | 6aababd | 2010-01-15 17:36:48 +0100 | [diff] [blame] | 231 | * | 
|  | 232 | * For setups with variable MCLKs, pass 0 as 'freq' argument. This will cause | 
|  | 233 | * theoretically possible sample rates to be enabled. Call it again with a | 
|  | 234 | * proper value set one the external clock is set (most probably you would do | 
|  | 235 | * that from a machine's driver 'hw_param' hook. | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 236 | */ | 
| Liam Girdwood | e550e17 | 2008-07-07 16:07:52 +0100 | [diff] [blame] | 237 | static int cs4270_set_dai_sysclk(struct snd_soc_dai *codec_dai, | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 238 | int clk_id, unsigned int freq, int dir) | 
|  | 239 | { | 
|  | 240 | struct snd_soc_codec *codec = codec_dai->codec; | 
| Mark Brown | b2c812e | 2010-04-14 15:35:19 +0900 | [diff] [blame] | 241 | struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec); | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 242 |  | 
|  | 243 | cs4270->mclk = freq; | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 244 | return 0; | 
|  | 245 | } | 
|  | 246 |  | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 247 | /** | 
|  | 248 | * cs4270_set_dai_fmt - configure the codec for the selected audio format | 
|  | 249 | * @codec_dai: the codec DAI | 
|  | 250 | * @format: a SND_SOC_DAIFMT_x value indicating the data format | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 251 | * | 
|  | 252 | * This function takes a bitmask of SND_SOC_DAIFMT_x bits and programs the | 
|  | 253 | * codec accordingly. | 
|  | 254 | * | 
|  | 255 | * Currently, this function only supports SND_SOC_DAIFMT_I2S and | 
|  | 256 | * SND_SOC_DAIFMT_LEFT_J.  The CS4270 codec also supports right-justified | 
|  | 257 | * data for playback only, but ASoC currently does not support different | 
|  | 258 | * formats for playback vs. record. | 
|  | 259 | */ | 
| Liam Girdwood | e550e17 | 2008-07-07 16:07:52 +0100 | [diff] [blame] | 260 | static int cs4270_set_dai_fmt(struct snd_soc_dai *codec_dai, | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 261 | unsigned int format) | 
|  | 262 | { | 
|  | 263 | struct snd_soc_codec *codec = codec_dai->codec; | 
| Mark Brown | b2c812e | 2010-04-14 15:35:19 +0900 | [diff] [blame] | 264 | struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec); | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 265 | int ret = 0; | 
|  | 266 |  | 
| Daniel Mack | 4eae080 | 2009-02-25 14:37:21 +0100 | [diff] [blame] | 267 | /* set DAI format */ | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 268 | switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { | 
|  | 269 | case SND_SOC_DAIFMT_I2S: | 
|  | 270 | case SND_SOC_DAIFMT_LEFT_J: | 
|  | 271 | cs4270->mode = format & SND_SOC_DAIFMT_FORMAT_MASK; | 
|  | 272 | break; | 
|  | 273 | default: | 
| Timur Tabi | a6c255e | 2009-02-02 15:08:29 -0600 | [diff] [blame] | 274 | dev_err(codec->dev, "invalid dai format\n"); | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 275 | ret = -EINVAL; | 
|  | 276 | } | 
|  | 277 |  | 
| Daniel Mack | 4eae080 | 2009-02-25 14:37:21 +0100 | [diff] [blame] | 278 | /* set master/slave audio interface */ | 
|  | 279 | switch (format & SND_SOC_DAIFMT_MASTER_MASK) { | 
|  | 280 | case SND_SOC_DAIFMT_CBS_CFS: | 
|  | 281 | cs4270->slave_mode = 1; | 
|  | 282 | break; | 
|  | 283 | case SND_SOC_DAIFMT_CBM_CFM: | 
|  | 284 | cs4270->slave_mode = 0; | 
|  | 285 | break; | 
| Daniel Mack | 4eae080 | 2009-02-25 14:37:21 +0100 | [diff] [blame] | 286 | default: | 
| Daniel Mack | ff09d49 | 2009-02-28 13:21:03 +0100 | [diff] [blame] | 287 | /* all other modes are unsupported by the hardware */ | 
| Daniel Mack | 4eae080 | 2009-02-25 14:37:21 +0100 | [diff] [blame] | 288 | ret = -EINVAL; | 
|  | 289 | } | 
|  | 290 |  | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 291 | return ret; | 
|  | 292 | } | 
|  | 293 |  | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 294 | /** | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 295 | * cs4270_hw_params - program the CS4270 with the given hardware parameters. | 
|  | 296 | * @substream: the audio stream | 
|  | 297 | * @params: the hardware parameters to set | 
|  | 298 | * @dai: the SOC DAI (ignored) | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 299 | * | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 300 | * This function programs the hardware with the values provided. | 
|  | 301 | * Specifically, the sample rate and the data format. | 
|  | 302 | * | 
|  | 303 | * The .ops functions are used to provide board-specific data, like input | 
|  | 304 | * frequencies, to this driver.  This function takes that information, | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 305 | * combines it with the hardware parameters provided, and programs the | 
|  | 306 | * hardware accordingly. | 
|  | 307 | */ | 
|  | 308 | static int cs4270_hw_params(struct snd_pcm_substream *substream, | 
| Mark Brown | dee89c4 | 2008-11-18 22:11:38 +0000 | [diff] [blame] | 309 | struct snd_pcm_hw_params *params, | 
|  | 310 | struct snd_soc_dai *dai) | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 311 | { | 
|  | 312 | struct snd_soc_pcm_runtime *rtd = substream->private_data; | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 313 | struct snd_soc_codec *codec = rtd->codec; | 
| Mark Brown | b2c812e | 2010-04-14 15:35:19 +0900 | [diff] [blame] | 314 | struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec); | 
| Roel Kluin | e34ba21 | 2008-04-17 18:58:34 +0200 | [diff] [blame] | 315 | int ret; | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 316 | unsigned int i; | 
|  | 317 | unsigned int rate; | 
|  | 318 | unsigned int ratio; | 
|  | 319 | int reg; | 
|  | 320 |  | 
|  | 321 | /* Figure out which MCLK/LRCK ratio to use */ | 
|  | 322 |  | 
|  | 323 | rate = params_rate(params);	/* Sampling rate, in Hz */ | 
|  | 324 | ratio = cs4270->mclk / rate;	/* MCLK/LRCK ratio */ | 
|  | 325 |  | 
| Timur Tabi | 9dbd627 | 2007-08-01 12:22:07 +0200 | [diff] [blame] | 326 | for (i = 0; i < NUM_MCLK_RATIOS; i++) { | 
| Timur Tabi | 8432395 | 2007-12-18 15:42:53 +0100 | [diff] [blame] | 327 | if (cs4270_mode_ratios[i].ratio == ratio) | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 328 | break; | 
|  | 329 | } | 
|  | 330 |  | 
| Timur Tabi | 9dbd627 | 2007-08-01 12:22:07 +0200 | [diff] [blame] | 331 | if (i == NUM_MCLK_RATIOS) { | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 332 | /* We did not find a matching ratio */ | 
| Timur Tabi | a6c255e | 2009-02-02 15:08:29 -0600 | [diff] [blame] | 333 | dev_err(codec->dev, "could not find matching ratio\n"); | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 334 | return -EINVAL; | 
|  | 335 | } | 
|  | 336 |  | 
| Timur Tabi | d5e9ba1 | 2009-02-03 11:09:32 -0600 | [diff] [blame] | 337 | /* Set the sample rate */ | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 338 |  | 
|  | 339 | reg = snd_soc_read(codec, CS4270_MODE); | 
|  | 340 | reg &= ~(CS4270_MODE_SPEED_MASK | CS4270_MODE_DIV_MASK); | 
| Daniel Mack | 4eae080 | 2009-02-25 14:37:21 +0100 | [diff] [blame] | 341 | reg |= cs4270_mode_ratios[i].mclk; | 
|  | 342 |  | 
|  | 343 | if (cs4270->slave_mode) | 
|  | 344 | reg |= CS4270_MODE_SLAVE; | 
|  | 345 | else | 
|  | 346 | reg |= cs4270_mode_ratios[i].speed_mode; | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 347 |  | 
|  | 348 | ret = snd_soc_write(codec, CS4270_MODE, reg); | 
|  | 349 | if (ret < 0) { | 
| Timur Tabi | a6c255e | 2009-02-02 15:08:29 -0600 | [diff] [blame] | 350 | dev_err(codec->dev, "i2c write failed\n"); | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 351 | return ret; | 
|  | 352 | } | 
|  | 353 |  | 
| Timur Tabi | d5e9ba1 | 2009-02-03 11:09:32 -0600 | [diff] [blame] | 354 | /* Set the DAI format */ | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 355 |  | 
|  | 356 | reg = snd_soc_read(codec, CS4270_FORMAT); | 
|  | 357 | reg &= ~(CS4270_FORMAT_DAC_MASK | CS4270_FORMAT_ADC_MASK); | 
|  | 358 |  | 
|  | 359 | switch (cs4270->mode) { | 
|  | 360 | case SND_SOC_DAIFMT_I2S: | 
|  | 361 | reg |= CS4270_FORMAT_DAC_I2S | CS4270_FORMAT_ADC_I2S; | 
|  | 362 | break; | 
|  | 363 | case SND_SOC_DAIFMT_LEFT_J: | 
|  | 364 | reg |= CS4270_FORMAT_DAC_LJ | CS4270_FORMAT_ADC_LJ; | 
|  | 365 | break; | 
|  | 366 | default: | 
| Timur Tabi | a6c255e | 2009-02-02 15:08:29 -0600 | [diff] [blame] | 367 | dev_err(codec->dev, "unknown dai format\n"); | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 368 | return -EINVAL; | 
|  | 369 | } | 
|  | 370 |  | 
|  | 371 | ret = snd_soc_write(codec, CS4270_FORMAT, reg); | 
|  | 372 | if (ret < 0) { | 
| Timur Tabi | a6c255e | 2009-02-02 15:08:29 -0600 | [diff] [blame] | 373 | dev_err(codec->dev, "i2c write failed\n"); | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 374 | return ret; | 
|  | 375 | } | 
|  | 376 |  | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 377 | return ret; | 
|  | 378 | } | 
|  | 379 |  | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 380 | /** | 
| Daniel Mack | 1a4ba05 | 2009-04-24 16:37:45 +0200 | [diff] [blame] | 381 | * cs4270_dai_mute - enable/disable the CS4270 external mute | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 382 | * @dai: the SOC DAI | 
|  | 383 | * @mute: 0 = disable mute, 1 = enable mute | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 384 | * | 
|  | 385 | * This function toggles the mute bits in the MUTE register.  The CS4270's | 
|  | 386 | * mute capability is intended for external muting circuitry, so if the | 
|  | 387 | * board does not have the MUTEA or MUTEB pins connected to such circuitry, | 
|  | 388 | * then this function will do nothing. | 
|  | 389 | */ | 
| Daniel Mack | 1a4ba05 | 2009-04-24 16:37:45 +0200 | [diff] [blame] | 390 | static int cs4270_dai_mute(struct snd_soc_dai *dai, int mute) | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 391 | { | 
|  | 392 | struct snd_soc_codec *codec = dai->codec; | 
| Mark Brown | b2c812e | 2010-04-14 15:35:19 +0900 | [diff] [blame] | 393 | struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec); | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 394 | int reg6; | 
|  | 395 |  | 
|  | 396 | reg6 = snd_soc_read(codec, CS4270_MUTE); | 
|  | 397 |  | 
|  | 398 | if (mute) | 
| Timur Tabi | d5e9ba1 | 2009-02-03 11:09:32 -0600 | [diff] [blame] | 399 | reg6 |= CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B; | 
| Daniel Mack | 1a4ba05 | 2009-04-24 16:37:45 +0200 | [diff] [blame] | 400 | else { | 
| Timur Tabi | d5e9ba1 | 2009-02-03 11:09:32 -0600 | [diff] [blame] | 401 | reg6 &= ~(CS4270_MUTE_DAC_A | CS4270_MUTE_DAC_B); | 
| Daniel Mack | 1a4ba05 | 2009-04-24 16:37:45 +0200 | [diff] [blame] | 402 | reg6 |= cs4270->manual_mute; | 
|  | 403 | } | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 404 |  | 
|  | 405 | return snd_soc_write(codec, CS4270_MUTE, reg6); | 
|  | 406 | } | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 407 |  | 
| Daniel Mack | 1a4ba05 | 2009-04-24 16:37:45 +0200 | [diff] [blame] | 408 | /** | 
|  | 409 | * cs4270_soc_put_mute - put callback for the 'Master Playback switch' | 
|  | 410 | * 			 alsa control. | 
|  | 411 | * @kcontrol: mixer control | 
|  | 412 | * @ucontrol: control element information | 
|  | 413 | * | 
|  | 414 | * This function basically passes the arguments on to the generic | 
|  | 415 | * snd_soc_put_volsw() function and saves the mute information in | 
|  | 416 | * our private data structure. This is because we want to prevent | 
|  | 417 | * cs4270_dai_mute() neglecting the user's decision to manually | 
|  | 418 | * mute the codec's output. | 
|  | 419 | * | 
|  | 420 | * Returns 0 for success. | 
|  | 421 | */ | 
|  | 422 | static int cs4270_soc_put_mute(struct snd_kcontrol *kcontrol, | 
|  | 423 | struct snd_ctl_elem_value *ucontrol) | 
|  | 424 | { | 
|  | 425 | struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); | 
| Mark Brown | b2c812e | 2010-04-14 15:35:19 +0900 | [diff] [blame] | 426 | struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec); | 
| Daniel Mack | 1a4ba05 | 2009-04-24 16:37:45 +0200 | [diff] [blame] | 427 | int left = !ucontrol->value.integer.value[0]; | 
|  | 428 | int right = !ucontrol->value.integer.value[1]; | 
|  | 429 |  | 
|  | 430 | cs4270->manual_mute = (left ? CS4270_MUTE_DAC_A : 0) | | 
|  | 431 | (right ? CS4270_MUTE_DAC_B : 0); | 
|  | 432 |  | 
|  | 433 | return snd_soc_put_volsw(kcontrol, ucontrol); | 
|  | 434 | } | 
|  | 435 |  | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 436 | /* A list of non-DAPM controls that the CS4270 supports */ | 
|  | 437 | static const struct snd_kcontrol_new cs4270_snd_controls[] = { | 
|  | 438 | SOC_DOUBLE_R("Master Playback Volume", | 
| Timur Tabi | d5e9ba1 | 2009-02-03 11:09:32 -0600 | [diff] [blame] | 439 | CS4270_VOLA, CS4270_VOLB, 0, 0xFF, 1), | 
|  | 440 | SOC_SINGLE("Digital Sidetone Switch", CS4270_FORMAT, 5, 1, 0), | 
|  | 441 | SOC_SINGLE("Soft Ramp Switch", CS4270_TRANS, 6, 1, 0), | 
|  | 442 | SOC_SINGLE("Zero Cross Switch", CS4270_TRANS, 5, 1, 0), | 
| Daniel Mack | 7e1aa1d | 2009-10-29 02:24:32 +0100 | [diff] [blame] | 443 | SOC_SINGLE("De-emphasis filter", CS4270_TRANS, 0, 1, 0), | 
| Timur Tabi | d5e9ba1 | 2009-02-03 11:09:32 -0600 | [diff] [blame] | 444 | SOC_SINGLE("Popguard Switch", CS4270_MODE, 0, 1, 1), | 
|  | 445 | SOC_SINGLE("Auto-Mute Switch", CS4270_MUTE, 5, 1, 0), | 
| Daniel Mack | 1a4ba05 | 2009-04-24 16:37:45 +0200 | [diff] [blame] | 446 | SOC_DOUBLE("Master Capture Switch", CS4270_MUTE, 3, 4, 1, 1), | 
|  | 447 | SOC_DOUBLE_EXT("Master Playback Switch", CS4270_MUTE, 0, 1, 1, 1, | 
|  | 448 | snd_soc_get_volsw, cs4270_soc_put_mute), | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 449 | }; | 
|  | 450 |  | 
| Eric Miao | 6335d05 | 2009-03-03 09:41:00 +0800 | [diff] [blame] | 451 | static struct snd_soc_dai_ops cs4270_dai_ops = { | 
|  | 452 | .hw_params	= cs4270_hw_params, | 
|  | 453 | .set_sysclk	= cs4270_set_dai_sysclk, | 
|  | 454 | .set_fmt	= cs4270_set_dai_fmt, | 
| Daniel Mack | 1a4ba05 | 2009-04-24 16:37:45 +0200 | [diff] [blame] | 455 | .digital_mute	= cs4270_dai_mute, | 
| Eric Miao | 6335d05 | 2009-03-03 09:41:00 +0800 | [diff] [blame] | 456 | }; | 
|  | 457 |  | 
| Mark Brown | 5c75848 | 2010-10-06 16:18:17 -0700 | [diff] [blame] | 458 | static struct snd_soc_dai_driver cs4270_dai = { | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 459 | .name = "cs4270-hifi", | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 460 | .playback = { | 
|  | 461 | .stream_name = "Playback", | 
|  | 462 | .channels_min = 1, | 
|  | 463 | .channels_max = 2, | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 464 | .rates = SNDRV_PCM_RATE_CONTINUOUS, | 
|  | 465 | .rate_min = 4000, | 
|  | 466 | .rate_max = 216000, | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 467 | .formats = CS4270_FORMATS, | 
|  | 468 | }, | 
|  | 469 | .capture = { | 
|  | 470 | .stream_name = "Capture", | 
|  | 471 | .channels_min = 1, | 
|  | 472 | .channels_max = 2, | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 473 | .rates = SNDRV_PCM_RATE_CONTINUOUS, | 
|  | 474 | .rate_min = 4000, | 
|  | 475 | .rate_max = 216000, | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 476 | .formats = CS4270_FORMATS, | 
|  | 477 | }, | 
| Eric Miao | 6335d05 | 2009-03-03 09:41:00 +0800 | [diff] [blame] | 478 | .ops = &cs4270_dai_ops, | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 479 | }; | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 480 |  | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 481 | /** | 
|  | 482 | * cs4270_probe - ASoC probe function | 
|  | 483 | * @pdev: platform device | 
|  | 484 | * | 
|  | 485 | * This function is called when ASoC has all the pieces it needs to | 
|  | 486 | * instantiate a sound driver. | 
| Timur Tabi | 04eb093 | 2009-01-29 14:28:37 -0600 | [diff] [blame] | 487 | */ | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 488 | static int cs4270_probe(struct snd_soc_codec *codec) | 
| Timur Tabi | 04eb093 | 2009-01-29 14:28:37 -0600 | [diff] [blame] | 489 | { | 
| Mark Brown | b2c812e | 2010-04-14 15:35:19 +0900 | [diff] [blame] | 490 | struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec); | 
| Timur Tabi | 11b8fca | 2011-01-10 13:28:32 -0600 | [diff] [blame] | 491 | int i, ret; | 
| Timur Tabi | 04eb093 | 2009-01-29 14:28:37 -0600 | [diff] [blame] | 492 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 493 | codec->control_data = cs4270->control_data; | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 494 |  | 
| Timur Tabi | 11b8fca | 2011-01-10 13:28:32 -0600 | [diff] [blame] | 495 | /* Tell ASoC what kind of I/O to use to read the registers.  ASoC will | 
|  | 496 | * then do the I2C transactions itself. | 
|  | 497 | */ | 
|  | 498 | ret = snd_soc_codec_set_cache_io(codec, 8, 8, cs4270->control_type); | 
| Timur Tabi | 0db4d07 | 2009-01-23 16:31:19 -0600 | [diff] [blame] | 499 | if (ret < 0) { | 
| Timur Tabi | 11b8fca | 2011-01-10 13:28:32 -0600 | [diff] [blame] | 500 | dev_err(codec->dev, "failed to set cache I/O (ret=%i)\n", ret); | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 501 | return ret; | 
| Timur Tabi | 0db4d07 | 2009-01-23 16:31:19 -0600 | [diff] [blame] | 502 | } | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 503 |  | 
| Timur Tabi | d5e9ba1 | 2009-02-03 11:09:32 -0600 | [diff] [blame] | 504 | /* Disable auto-mute.  This feature appears to be buggy.  In some | 
|  | 505 | * situations, auto-mute will not deactivate when it should, so we want | 
|  | 506 | * this feature disabled by default.  An application (e.g. alsactl) can | 
|  | 507 | * re-enabled it by using the controls. | 
|  | 508 | */ | 
| Timur Tabi | 11b8fca | 2011-01-10 13:28:32 -0600 | [diff] [blame] | 509 | ret = snd_soc_update_bits(codec, CS4270_MUTE, CS4270_MUTE_AUTO, 0); | 
| Timur Tabi | d5e9ba1 | 2009-02-03 11:09:32 -0600 | [diff] [blame] | 510 | if (ret < 0) { | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 511 | dev_err(codec->dev, "i2c write failed\n"); | 
| Timur Tabi | d5e9ba1 | 2009-02-03 11:09:32 -0600 | [diff] [blame] | 512 | return ret; | 
|  | 513 | } | 
|  | 514 |  | 
|  | 515 | /* Disable automatic volume control.  The hardware enables, and it | 
|  | 516 | * causes volume change commands to be delayed, sometimes until after | 
|  | 517 | * playback has started.  An application (e.g. alsactl) can | 
|  | 518 | * re-enabled it by using the controls. | 
|  | 519 | */ | 
| Timur Tabi | 11b8fca | 2011-01-10 13:28:32 -0600 | [diff] [blame] | 520 | ret = snd_soc_update_bits(codec, CS4270_TRANS, | 
|  | 521 | CS4270_TRANS_SOFT | CS4270_TRANS_ZERO, 0); | 
| Timur Tabi | d5e9ba1 | 2009-02-03 11:09:32 -0600 | [diff] [blame] | 522 | if (ret < 0) { | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 523 | dev_err(codec->dev, "i2c write failed\n"); | 
| Timur Tabi | d5e9ba1 | 2009-02-03 11:09:32 -0600 | [diff] [blame] | 524 | return ret; | 
|  | 525 | } | 
|  | 526 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 527 | /* Add the non-DAPM controls */ | 
|  | 528 | ret = snd_soc_add_controls(codec, cs4270_snd_controls, | 
|  | 529 | ARRAY_SIZE(cs4270_snd_controls)); | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 530 | if (ret < 0) { | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 531 | dev_err(codec->dev, "failed to add controls\n"); | 
|  | 532 | return ret; | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 533 | } | 
|  | 534 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 535 | /* get the power supply regulators */ | 
|  | 536 | for (i = 0; i < ARRAY_SIZE(supply_names); i++) | 
|  | 537 | cs4270->supplies[i].supply = supply_names[i]; | 
|  | 538 |  | 
|  | 539 | ret = regulator_bulk_get(codec->dev, ARRAY_SIZE(cs4270->supplies), | 
|  | 540 | cs4270->supplies); | 
|  | 541 | if (ret < 0) | 
|  | 542 | return ret; | 
|  | 543 |  | 
|  | 544 | ret = regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies), | 
|  | 545 | cs4270->supplies); | 
|  | 546 | if (ret < 0) | 
|  | 547 | goto error_free_regulators; | 
| Jean Delvare | e3145df | 2008-09-30 11:40:37 +0200 | [diff] [blame] | 548 |  | 
| Timur Tabi | 0db4d07 | 2009-01-23 16:31:19 -0600 | [diff] [blame] | 549 | return 0; | 
| Jean Delvare | e3145df | 2008-09-30 11:40:37 +0200 | [diff] [blame] | 550 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 551 | error_free_regulators: | 
|  | 552 | regulator_bulk_free(ARRAY_SIZE(cs4270->supplies), | 
|  | 553 | cs4270->supplies); | 
| Jean Delvare | e3145df | 2008-09-30 11:40:37 +0200 | [diff] [blame] | 554 |  | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 555 | return ret; | 
|  | 556 | } | 
|  | 557 |  | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 558 | /** | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 559 | * cs4270_remove - ASoC remove function | 
|  | 560 | * @pdev: platform device | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 561 | * | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 562 | * This function is the counterpart to cs4270_probe(). | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 563 | */ | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 564 | static int cs4270_remove(struct snd_soc_codec *codec) | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 565 | { | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 566 | struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec); | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 567 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 568 | regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies), cs4270->supplies); | 
|  | 569 | regulator_bulk_free(ARRAY_SIZE(cs4270->supplies), cs4270->supplies); | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 570 |  | 
| Timur Tabi | 0db4d07 | 2009-01-23 16:31:19 -0600 | [diff] [blame] | 571 | return 0; | 
| Timur Tabi | 0db4d07 | 2009-01-23 16:31:19 -0600 | [diff] [blame] | 572 | }; | 
| Timur Tabi | 0db4d07 | 2009-01-23 16:31:19 -0600 | [diff] [blame] | 573 |  | 
| Daniel Mack | 5e7c034 | 2009-05-06 01:26:01 +0200 | [diff] [blame] | 574 | #ifdef CONFIG_PM | 
|  | 575 |  | 
|  | 576 | /* This suspend/resume implementation can handle both - a simple standby | 
|  | 577 | * where the codec remains powered, and a full suspend, where the voltage | 
|  | 578 | * domain the codec is connected to is teared down and/or any other hardware | 
|  | 579 | * reset condition is asserted. | 
|  | 580 | * | 
|  | 581 | * The codec's own power saving features are enabled in the suspend callback, | 
|  | 582 | * and all registers are written back to the hardware when resuming. | 
|  | 583 | */ | 
|  | 584 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 585 | static int cs4270_soc_suspend(struct snd_soc_codec *codec, pm_message_t mesg) | 
| Daniel Mack | 15b5bda | 2009-08-05 20:50:43 +0200 | [diff] [blame] | 586 | { | 
| Mark Brown | b2c812e | 2010-04-14 15:35:19 +0900 | [diff] [blame] | 587 | struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec); | 
| Daniel Mack | ffbfd33 | 2009-11-30 17:56:11 +0100 | [diff] [blame] | 588 | int reg, ret; | 
| Daniel Mack | 15b5bda | 2009-08-05 20:50:43 +0200 | [diff] [blame] | 589 |  | 
| Daniel Mack | ffbfd33 | 2009-11-30 17:56:11 +0100 | [diff] [blame] | 590 | reg = snd_soc_read(codec, CS4270_PWRCTL) | CS4270_PWRCTL_PDN_ALL; | 
|  | 591 | if (reg < 0) | 
|  | 592 | return reg; | 
|  | 593 |  | 
|  | 594 | ret = snd_soc_write(codec, CS4270_PWRCTL, reg); | 
|  | 595 | if (ret < 0) | 
|  | 596 | return ret; | 
|  | 597 |  | 
|  | 598 | regulator_bulk_disable(ARRAY_SIZE(cs4270->supplies), | 
|  | 599 | cs4270->supplies); | 
|  | 600 |  | 
|  | 601 | return 0; | 
| Daniel Mack | 15b5bda | 2009-08-05 20:50:43 +0200 | [diff] [blame] | 602 | } | 
|  | 603 |  | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 604 | static int cs4270_soc_resume(struct snd_soc_codec *codec) | 
| Daniel Mack | 15b5bda | 2009-08-05 20:50:43 +0200 | [diff] [blame] | 605 | { | 
| Mark Brown | b2c812e | 2010-04-14 15:35:19 +0900 | [diff] [blame] | 606 | struct cs4270_private *cs4270 = snd_soc_codec_get_drvdata(codec); | 
| Daniel Mack | 15b5bda | 2009-08-05 20:50:43 +0200 | [diff] [blame] | 607 | struct i2c_client *i2c_client = codec->control_data; | 
| Daniel Mack | 5e7c034 | 2009-05-06 01:26:01 +0200 | [diff] [blame] | 608 | int reg; | 
|  | 609 |  | 
| Daniel Mack | ffbfd33 | 2009-11-30 17:56:11 +0100 | [diff] [blame] | 610 | regulator_bulk_enable(ARRAY_SIZE(cs4270->supplies), | 
|  | 611 | cs4270->supplies); | 
|  | 612 |  | 
| Daniel Mack | 5e7c034 | 2009-05-06 01:26:01 +0200 | [diff] [blame] | 613 | /* In case the device was put to hard reset during sleep, we need to | 
|  | 614 | * wait 500ns here before any I2C communication. */ | 
|  | 615 | ndelay(500); | 
|  | 616 |  | 
|  | 617 | /* first restore the entire register cache ... */ | 
|  | 618 | for (reg = CS4270_FIRSTREG; reg <= CS4270_LASTREG; reg++) { | 
|  | 619 | u8 val = snd_soc_read(codec, reg); | 
|  | 620 |  | 
| Daniel Mack | 15b5bda | 2009-08-05 20:50:43 +0200 | [diff] [blame] | 621 | if (i2c_smbus_write_byte_data(i2c_client, reg, val)) { | 
| Daniel Mack | 5e7c034 | 2009-05-06 01:26:01 +0200 | [diff] [blame] | 622 | dev_err(codec->dev, "i2c write failed\n"); | 
|  | 623 | return -EIO; | 
|  | 624 | } | 
|  | 625 | } | 
|  | 626 |  | 
|  | 627 | /* ... then disable the power-down bits */ | 
|  | 628 | reg = snd_soc_read(codec, CS4270_PWRCTL); | 
|  | 629 | reg &= ~CS4270_PWRCTL_PDN_ALL; | 
|  | 630 |  | 
|  | 631 | return snd_soc_write(codec, CS4270_PWRCTL, reg); | 
|  | 632 | } | 
|  | 633 | #else | 
| Daniel Mack | 15b5bda | 2009-08-05 20:50:43 +0200 | [diff] [blame] | 634 | #define cs4270_soc_suspend	NULL | 
|  | 635 | #define cs4270_soc_resume	NULL | 
| Daniel Mack | 5e7c034 | 2009-05-06 01:26:01 +0200 | [diff] [blame] | 636 | #endif /* CONFIG_PM */ | 
|  | 637 |  | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 638 | /* | 
| Daniel Mack | b6f7d7c | 2011-05-25 09:53:12 +0200 | [diff] [blame^] | 639 | * ASoC codec driver structure | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 640 | */ | 
| Timur Tabi | 11b8fca | 2011-01-10 13:28:32 -0600 | [diff] [blame] | 641 | static const struct snd_soc_codec_driver soc_codec_device_cs4270 = { | 
|  | 642 | .probe =		cs4270_probe, | 
|  | 643 | .remove =		cs4270_remove, | 
|  | 644 | .suspend =		cs4270_soc_suspend, | 
|  | 645 | .resume =		cs4270_soc_resume, | 
|  | 646 | .volatile_register =	cs4270_reg_is_volatile, | 
|  | 647 | .readable_register =	cs4270_reg_is_readable, | 
|  | 648 | .reg_cache_size =	CS4270_LASTREG + 1, | 
|  | 649 | .reg_word_size =	sizeof(u8), | 
|  | 650 | .reg_cache_default =	cs4270_default_reg_cache, | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 651 | }; | 
|  | 652 |  | 
|  | 653 | /** | 
|  | 654 | * cs4270_i2c_probe - initialize the I2C interface of the CS4270 | 
|  | 655 | * @i2c_client: the I2C client object | 
|  | 656 | * @id: the I2C device ID (ignored) | 
|  | 657 | * | 
|  | 658 | * This function is called whenever the I2C subsystem finds a device that | 
|  | 659 | * matches the device ID given via a prior call to i2c_add_driver(). | 
|  | 660 | */ | 
|  | 661 | static int cs4270_i2c_probe(struct i2c_client *i2c_client, | 
|  | 662 | const struct i2c_device_id *id) | 
|  | 663 | { | 
|  | 664 | struct cs4270_private *cs4270; | 
|  | 665 | int ret; | 
|  | 666 |  | 
|  | 667 | /* Verify that we have a CS4270 */ | 
|  | 668 |  | 
|  | 669 | ret = i2c_smbus_read_byte_data(i2c_client, CS4270_CHIPID); | 
|  | 670 | if (ret < 0) { | 
|  | 671 | dev_err(&i2c_client->dev, "failed to read i2c at addr %X\n", | 
|  | 672 | i2c_client->addr); | 
|  | 673 | return ret; | 
|  | 674 | } | 
|  | 675 | /* The top four bits of the chip ID should be 1100. */ | 
|  | 676 | if ((ret & 0xF0) != 0xC0) { | 
|  | 677 | dev_err(&i2c_client->dev, "device at addr %X is not a CS4270\n", | 
|  | 678 | i2c_client->addr); | 
|  | 679 | return -ENODEV; | 
|  | 680 | } | 
|  | 681 |  | 
|  | 682 | dev_info(&i2c_client->dev, "found device at i2c address %X\n", | 
|  | 683 | i2c_client->addr); | 
|  | 684 | dev_info(&i2c_client->dev, "hardware revision %X\n", ret & 0xF); | 
|  | 685 |  | 
|  | 686 | cs4270 = kzalloc(sizeof(struct cs4270_private), GFP_KERNEL); | 
|  | 687 | if (!cs4270) { | 
|  | 688 | dev_err(&i2c_client->dev, "could not allocate codec\n"); | 
|  | 689 | return -ENOMEM; | 
|  | 690 | } | 
|  | 691 |  | 
|  | 692 | i2c_set_clientdata(i2c_client, cs4270); | 
|  | 693 | cs4270->control_data = i2c_client; | 
|  | 694 | cs4270->control_type = SND_SOC_I2C; | 
|  | 695 |  | 
|  | 696 | ret = snd_soc_register_codec(&i2c_client->dev, | 
|  | 697 | &soc_codec_device_cs4270, &cs4270_dai, 1); | 
|  | 698 | if (ret < 0) | 
|  | 699 | kfree(cs4270); | 
|  | 700 | return ret; | 
|  | 701 | } | 
|  | 702 |  | 
|  | 703 | /** | 
|  | 704 | * cs4270_i2c_remove - remove an I2C device | 
|  | 705 | * @i2c_client: the I2C client object | 
|  | 706 | * | 
|  | 707 | * This function is the counterpart to cs4270_i2c_probe(). | 
|  | 708 | */ | 
|  | 709 | static int cs4270_i2c_remove(struct i2c_client *i2c_client) | 
|  | 710 | { | 
|  | 711 | snd_soc_unregister_codec(&i2c_client->dev); | 
|  | 712 | kfree(i2c_get_clientdata(i2c_client)); | 
|  | 713 | return 0; | 
|  | 714 | } | 
|  | 715 |  | 
|  | 716 | /* | 
|  | 717 | * cs4270_id - I2C device IDs supported by this driver | 
|  | 718 | */ | 
| Axel Lin | 79a54ea | 2011-03-04 15:22:03 +0800 | [diff] [blame] | 719 | static const struct i2c_device_id cs4270_id[] = { | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 720 | {"cs4270", 0}, | 
|  | 721 | {} | 
|  | 722 | }; | 
|  | 723 | MODULE_DEVICE_TABLE(i2c, cs4270_id); | 
|  | 724 |  | 
|  | 725 | /* | 
| Timur Tabi | ff7bf02 | 2009-01-30 11:14:49 -0600 | [diff] [blame] | 726 | * cs4270_i2c_driver - I2C device identification | 
|  | 727 | * | 
|  | 728 | * This structure tells the I2C subsystem how to identify and support a | 
|  | 729 | * given I2C device type. | 
|  | 730 | */ | 
| Timur Tabi | 0db4d07 | 2009-01-23 16:31:19 -0600 | [diff] [blame] | 731 | static struct i2c_driver cs4270_i2c_driver = { | 
|  | 732 | .driver = { | 
| Liam Girdwood | f0fba2a | 2010-03-17 20:15:21 +0000 | [diff] [blame] | 733 | .name = "cs4270-codec", | 
| Timur Tabi | 0db4d07 | 2009-01-23 16:31:19 -0600 | [diff] [blame] | 734 | .owner = THIS_MODULE, | 
|  | 735 | }, | 
|  | 736 | .id_table = cs4270_id, | 
|  | 737 | .probe = cs4270_i2c_probe, | 
|  | 738 | .remove = cs4270_i2c_remove, | 
|  | 739 | }; | 
|  | 740 |  | 
| Takashi Iwai | c9b3a40 | 2008-12-10 07:47:22 +0100 | [diff] [blame] | 741 | static int __init cs4270_init(void) | 
| Mark Brown | 64089b8 | 2008-12-08 19:17:58 +0000 | [diff] [blame] | 742 | { | 
| Timur Tabi | 04eb093 | 2009-01-29 14:28:37 -0600 | [diff] [blame] | 743 | return i2c_add_driver(&cs4270_i2c_driver); | 
| Mark Brown | 64089b8 | 2008-12-08 19:17:58 +0000 | [diff] [blame] | 744 | } | 
|  | 745 | module_init(cs4270_init); | 
|  | 746 |  | 
|  | 747 | static void __exit cs4270_exit(void) | 
|  | 748 | { | 
| Timur Tabi | 04eb093 | 2009-01-29 14:28:37 -0600 | [diff] [blame] | 749 | i2c_del_driver(&cs4270_i2c_driver); | 
| Mark Brown | 64089b8 | 2008-12-08 19:17:58 +0000 | [diff] [blame] | 750 | } | 
|  | 751 | module_exit(cs4270_exit); | 
|  | 752 |  | 
| Timur Tabi | b0c813c | 2007-07-31 18:18:44 +0200 | [diff] [blame] | 753 | MODULE_AUTHOR("Timur Tabi <timur@freescale.com>"); | 
|  | 754 | MODULE_DESCRIPTION("Cirrus Logic CS4270 ALSA SoC Codec Driver"); | 
|  | 755 | MODULE_LICENSE("GPL"); |