| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (C) 2015 Andrea Venturi |
|---|
| 3 | 4 | * Andrea Venturi <be17068@iperbole.bo.it> |
|---|
| 4 | 5 | * |
|---|
| 5 | 6 | * Copyright (C) 2016 Maxime Ripard |
|---|
| 6 | 7 | * Maxime Ripard <maxime.ripard@free-electrons.com> |
|---|
| 7 | | - * |
|---|
| 8 | | - * This program is free software; you can redistribute it and/or |
|---|
| 9 | | - * modify it under the terms of the GNU General Public License as |
|---|
| 10 | | - * published by the Free Software Foundation; either version 2 of |
|---|
| 11 | | - * the License, or (at your option) any later version. |
|---|
| 12 | 8 | */ |
|---|
| 13 | 9 | |
|---|
| 14 | 10 | #include <linux/clk.h> |
|---|
| .. | .. |
|---|
| 50 | 46 | #define SUN4I_I2S_FMT0_FMT_RIGHT_J (2 << 0) |
|---|
| 51 | 47 | #define SUN4I_I2S_FMT0_FMT_LEFT_J (1 << 0) |
|---|
| 52 | 48 | #define SUN4I_I2S_FMT0_FMT_I2S (0 << 0) |
|---|
| 53 | | -#define SUN4I_I2S_FMT0_POLARITY_INVERTED (1) |
|---|
| 54 | | -#define SUN4I_I2S_FMT0_POLARITY_NORMAL (0) |
|---|
| 55 | 49 | |
|---|
| 56 | 50 | #define SUN4I_I2S_FMT1_REG 0x08 |
|---|
| 57 | 51 | #define SUN4I_I2S_FIFO_TX_REG 0x0c |
|---|
| .. | .. |
|---|
| 84 | 78 | #define SUN4I_I2S_RX_CNT_REG 0x2c |
|---|
| 85 | 79 | |
|---|
| 86 | 80 | #define SUN4I_I2S_TX_CHAN_SEL_REG 0x30 |
|---|
| 81 | +#define SUN4I_I2S_CHAN_SEL_MASK GENMASK(2, 0) |
|---|
| 87 | 82 | #define SUN4I_I2S_CHAN_SEL(num_chan) (((num_chan) - 1) << 0) |
|---|
| 88 | 83 | |
|---|
| 89 | 84 | #define SUN4I_I2S_TX_CHAN_MAP_REG 0x34 |
|---|
| .. | .. |
|---|
| 96 | 91 | #define SUN8I_I2S_CTRL_BCLK_OUT BIT(18) |
|---|
| 97 | 92 | #define SUN8I_I2S_CTRL_LRCK_OUT BIT(17) |
|---|
| 98 | 93 | |
|---|
| 94 | +#define SUN8I_I2S_CTRL_MODE_MASK GENMASK(5, 4) |
|---|
| 95 | +#define SUN8I_I2S_CTRL_MODE_RIGHT (2 << 4) |
|---|
| 96 | +#define SUN8I_I2S_CTRL_MODE_LEFT (1 << 4) |
|---|
| 97 | +#define SUN8I_I2S_CTRL_MODE_PCM (0 << 4) |
|---|
| 98 | + |
|---|
| 99 | +#define SUN8I_I2S_FMT0_LRCLK_POLARITY_MASK BIT(19) |
|---|
| 100 | +#define SUN8I_I2S_FMT0_LRCLK_POLARITY_INVERTED (1 << 19) |
|---|
| 101 | +#define SUN8I_I2S_FMT0_LRCLK_POLARITY_NORMAL (0 << 19) |
|---|
| 99 | 102 | #define SUN8I_I2S_FMT0_LRCK_PERIOD_MASK GENMASK(17, 8) |
|---|
| 100 | 103 | #define SUN8I_I2S_FMT0_LRCK_PERIOD(period) ((period - 1) << 8) |
|---|
| 104 | +#define SUN8I_I2S_FMT0_BCLK_POLARITY_MASK BIT(7) |
|---|
| 105 | +#define SUN8I_I2S_FMT0_BCLK_POLARITY_INVERTED (1 << 7) |
|---|
| 106 | +#define SUN8I_I2S_FMT0_BCLK_POLARITY_NORMAL (0 << 7) |
|---|
| 101 | 107 | |
|---|
| 102 | 108 | #define SUN8I_I2S_INT_STA_REG 0x0c |
|---|
| 103 | 109 | #define SUN8I_I2S_FIFO_TX_REG 0x20 |
|---|
| .. | .. |
|---|
| 118 | 124 | #define SUN8I_I2S_RX_CHAN_SEL_REG 0x54 |
|---|
| 119 | 125 | #define SUN8I_I2S_RX_CHAN_MAP_REG 0x58 |
|---|
| 120 | 126 | |
|---|
| 127 | +struct sun4i_i2s; |
|---|
| 128 | + |
|---|
| 121 | 129 | /** |
|---|
| 122 | 130 | * struct sun4i_i2s_quirks - Differences between SoC variants. |
|---|
| 123 | | - * |
|---|
| 124 | 131 | * @has_reset: SoC needs reset deasserted. |
|---|
| 125 | | - * @has_slave_select_bit: SoC has a bit to enable slave mode. |
|---|
| 126 | | - * @has_fmt_set_lrck_period: SoC requires lrclk period to be set. |
|---|
| 127 | | - * @has_chcfg: tx and rx slot number need to be set. |
|---|
| 128 | | - * @has_chsel_tx_chen: SoC requires that the tx channels are enabled. |
|---|
| 129 | | - * @has_chsel_offset: SoC uses offset for selecting dai operational mode. |
|---|
| 130 | 132 | * @reg_offset_txdata: offset of the tx fifo. |
|---|
| 131 | 133 | * @sun4i_i2s_regmap: regmap config to use. |
|---|
| 132 | | - * @mclk_offset: Value by which mclkdiv needs to be adjusted. |
|---|
| 133 | | - * @bclk_offset: Value by which bclkdiv needs to be adjusted. |
|---|
| 134 | | - * @fmt_offset: Value by which wss and sr needs to be adjusted. |
|---|
| 135 | 134 | * @field_clkdiv_mclk_en: regmap field to enable mclk output. |
|---|
| 136 | 135 | * @field_fmt_wss: regmap field to set word select size. |
|---|
| 137 | 136 | * @field_fmt_sr: regmap field to set sample resolution. |
|---|
| 138 | | - * @field_fmt_bclk: regmap field to set clk polarity. |
|---|
| 139 | | - * @field_fmt_lrclk: regmap field to set frame polarity. |
|---|
| 140 | | - * @field_fmt_mode: regmap field to set the operational mode. |
|---|
| 141 | | - * @field_txchanmap: location of the tx channel mapping register. |
|---|
| 142 | | - * @field_rxchanmap: location of the rx channel mapping register. |
|---|
| 143 | | - * @field_txchansel: location of the tx channel select bit fields. |
|---|
| 144 | | - * @field_rxchansel: location of the rx channel select bit fields. |
|---|
| 137 | + * @bclk_dividers: bit clock dividers array |
|---|
| 138 | + * @num_bclk_dividers: number of bit clock dividers |
|---|
| 139 | + * @mclk_dividers: mclk dividers array |
|---|
| 140 | + * @num_mclk_dividers: number of mclk dividers |
|---|
| 141 | + * @get_bclk_parent_rate: callback to get bclk parent rate |
|---|
| 142 | + * @get_sr: callback to get sample resolution |
|---|
| 143 | + * @get_wss: callback to get word select size |
|---|
| 144 | + * @set_chan_cfg: callback to set channel configuration |
|---|
| 145 | + * @set_fmt: callback to set format |
|---|
| 145 | 146 | */ |
|---|
| 146 | 147 | struct sun4i_i2s_quirks { |
|---|
| 147 | 148 | bool has_reset; |
|---|
| 148 | | - bool has_slave_select_bit; |
|---|
| 149 | | - bool has_fmt_set_lrck_period; |
|---|
| 150 | | - bool has_chcfg; |
|---|
| 151 | | - bool has_chsel_tx_chen; |
|---|
| 152 | | - bool has_chsel_offset; |
|---|
| 153 | 149 | unsigned int reg_offset_txdata; /* TX FIFO */ |
|---|
| 154 | 150 | const struct regmap_config *sun4i_i2s_regmap; |
|---|
| 155 | | - unsigned int mclk_offset; |
|---|
| 156 | | - unsigned int bclk_offset; |
|---|
| 157 | | - unsigned int fmt_offset; |
|---|
| 158 | 151 | |
|---|
| 159 | 152 | /* Register fields for i2s */ |
|---|
| 160 | 153 | struct reg_field field_clkdiv_mclk_en; |
|---|
| 161 | 154 | struct reg_field field_fmt_wss; |
|---|
| 162 | 155 | struct reg_field field_fmt_sr; |
|---|
| 163 | | - struct reg_field field_fmt_bclk; |
|---|
| 164 | | - struct reg_field field_fmt_lrclk; |
|---|
| 165 | | - struct reg_field field_fmt_mode; |
|---|
| 166 | | - struct reg_field field_txchanmap; |
|---|
| 167 | | - struct reg_field field_rxchanmap; |
|---|
| 168 | | - struct reg_field field_txchansel; |
|---|
| 169 | | - struct reg_field field_rxchansel; |
|---|
| 156 | + |
|---|
| 157 | + const struct sun4i_i2s_clk_div *bclk_dividers; |
|---|
| 158 | + unsigned int num_bclk_dividers; |
|---|
| 159 | + const struct sun4i_i2s_clk_div *mclk_dividers; |
|---|
| 160 | + unsigned int num_mclk_dividers; |
|---|
| 161 | + |
|---|
| 162 | + unsigned long (*get_bclk_parent_rate)(const struct sun4i_i2s *); |
|---|
| 163 | + s8 (*get_sr)(const struct sun4i_i2s *, int); |
|---|
| 164 | + s8 (*get_wss)(const struct sun4i_i2s *, int); |
|---|
| 165 | + int (*set_chan_cfg)(const struct sun4i_i2s *, |
|---|
| 166 | + const struct snd_pcm_hw_params *); |
|---|
| 167 | + int (*set_fmt)(const struct sun4i_i2s *, unsigned int); |
|---|
| 170 | 168 | }; |
|---|
| 171 | 169 | |
|---|
| 172 | 170 | struct sun4i_i2s { |
|---|
| .. | .. |
|---|
| 175 | 173 | struct regmap *regmap; |
|---|
| 176 | 174 | struct reset_control *rst; |
|---|
| 177 | 175 | |
|---|
| 176 | + unsigned int format; |
|---|
| 178 | 177 | unsigned int mclk_freq; |
|---|
| 178 | + unsigned int slots; |
|---|
| 179 | + unsigned int slot_width; |
|---|
| 179 | 180 | |
|---|
| 180 | 181 | struct snd_dmaengine_dai_dma_data capture_dma_data; |
|---|
| 181 | 182 | struct snd_dmaengine_dai_dma_data playback_dma_data; |
|---|
| .. | .. |
|---|
| 184 | 185 | struct regmap_field *field_clkdiv_mclk_en; |
|---|
| 185 | 186 | struct regmap_field *field_fmt_wss; |
|---|
| 186 | 187 | struct regmap_field *field_fmt_sr; |
|---|
| 187 | | - struct regmap_field *field_fmt_bclk; |
|---|
| 188 | | - struct regmap_field *field_fmt_lrclk; |
|---|
| 189 | | - struct regmap_field *field_fmt_mode; |
|---|
| 190 | | - struct regmap_field *field_txchanmap; |
|---|
| 191 | | - struct regmap_field *field_rxchanmap; |
|---|
| 192 | | - struct regmap_field *field_txchansel; |
|---|
| 193 | | - struct regmap_field *field_rxchansel; |
|---|
| 194 | 188 | |
|---|
| 195 | 189 | const struct sun4i_i2s_quirks *variant; |
|---|
| 196 | 190 | }; |
|---|
| .. | .. |
|---|
| 222 | 216 | /* TODO - extend divide ratio supported by newer SoCs */ |
|---|
| 223 | 217 | }; |
|---|
| 224 | 218 | |
|---|
| 219 | +static const struct sun4i_i2s_clk_div sun8i_i2s_clk_div[] = { |
|---|
| 220 | + { .div = 1, .val = 1 }, |
|---|
| 221 | + { .div = 2, .val = 2 }, |
|---|
| 222 | + { .div = 4, .val = 3 }, |
|---|
| 223 | + { .div = 6, .val = 4 }, |
|---|
| 224 | + { .div = 8, .val = 5 }, |
|---|
| 225 | + { .div = 12, .val = 6 }, |
|---|
| 226 | + { .div = 16, .val = 7 }, |
|---|
| 227 | + { .div = 24, .val = 8 }, |
|---|
| 228 | + { .div = 32, .val = 9 }, |
|---|
| 229 | + { .div = 48, .val = 10 }, |
|---|
| 230 | + { .div = 64, .val = 11 }, |
|---|
| 231 | + { .div = 96, .val = 12 }, |
|---|
| 232 | + { .div = 128, .val = 13 }, |
|---|
| 233 | + { .div = 176, .val = 14 }, |
|---|
| 234 | + { .div = 192, .val = 15 }, |
|---|
| 235 | +}; |
|---|
| 236 | + |
|---|
| 237 | +static unsigned long sun4i_i2s_get_bclk_parent_rate(const struct sun4i_i2s *i2s) |
|---|
| 238 | +{ |
|---|
| 239 | + return i2s->mclk_freq; |
|---|
| 240 | +} |
|---|
| 241 | + |
|---|
| 242 | +static unsigned long sun8i_i2s_get_bclk_parent_rate(const struct sun4i_i2s *i2s) |
|---|
| 243 | +{ |
|---|
| 244 | + return clk_get_rate(i2s->mod_clk); |
|---|
| 245 | +} |
|---|
| 246 | + |
|---|
| 225 | 247 | static int sun4i_i2s_get_bclk_div(struct sun4i_i2s *i2s, |
|---|
| 226 | 248 | unsigned long parent_rate, |
|---|
| 227 | 249 | unsigned int sampling_rate, |
|---|
| 250 | + unsigned int channels, |
|---|
| 228 | 251 | unsigned int word_size) |
|---|
| 229 | 252 | { |
|---|
| 230 | | - int div = parent_rate / sampling_rate / word_size / 2; |
|---|
| 253 | + const struct sun4i_i2s_clk_div *dividers = i2s->variant->bclk_dividers; |
|---|
| 254 | + int div = parent_rate / sampling_rate / word_size / channels; |
|---|
| 231 | 255 | int i; |
|---|
| 232 | 256 | |
|---|
| 233 | | - for (i = 0; i < ARRAY_SIZE(sun4i_i2s_bclk_div); i++) { |
|---|
| 234 | | - const struct sun4i_i2s_clk_div *bdiv = &sun4i_i2s_bclk_div[i]; |
|---|
| 257 | + for (i = 0; i < i2s->variant->num_bclk_dividers; i++) { |
|---|
| 258 | + const struct sun4i_i2s_clk_div *bdiv = ÷rs[i]; |
|---|
| 235 | 259 | |
|---|
| 236 | 260 | if (bdiv->div == div) |
|---|
| 237 | 261 | return bdiv->val; |
|---|
| .. | .. |
|---|
| 241 | 265 | } |
|---|
| 242 | 266 | |
|---|
| 243 | 267 | static int sun4i_i2s_get_mclk_div(struct sun4i_i2s *i2s, |
|---|
| 244 | | - unsigned int oversample_rate, |
|---|
| 245 | | - unsigned int module_rate, |
|---|
| 246 | | - unsigned int sampling_rate) |
|---|
| 268 | + unsigned long parent_rate, |
|---|
| 269 | + unsigned long mclk_rate) |
|---|
| 247 | 270 | { |
|---|
| 248 | | - int div = module_rate / sampling_rate / oversample_rate; |
|---|
| 271 | + const struct sun4i_i2s_clk_div *dividers = i2s->variant->mclk_dividers; |
|---|
| 272 | + int div = parent_rate / mclk_rate; |
|---|
| 249 | 273 | int i; |
|---|
| 250 | 274 | |
|---|
| 251 | | - for (i = 0; i < ARRAY_SIZE(sun4i_i2s_mclk_div); i++) { |
|---|
| 252 | | - const struct sun4i_i2s_clk_div *mdiv = &sun4i_i2s_mclk_div[i]; |
|---|
| 275 | + for (i = 0; i < i2s->variant->num_mclk_dividers; i++) { |
|---|
| 276 | + const struct sun4i_i2s_clk_div *mdiv = ÷rs[i]; |
|---|
| 253 | 277 | |
|---|
| 254 | 278 | if (mdiv->div == div) |
|---|
| 255 | 279 | return mdiv->val; |
|---|
| .. | .. |
|---|
| 272 | 296 | |
|---|
| 273 | 297 | static int sun4i_i2s_set_clk_rate(struct snd_soc_dai *dai, |
|---|
| 274 | 298 | unsigned int rate, |
|---|
| 275 | | - unsigned int word_size) |
|---|
| 299 | + unsigned int slots, |
|---|
| 300 | + unsigned int slot_width) |
|---|
| 276 | 301 | { |
|---|
| 277 | 302 | struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
|---|
| 278 | | - unsigned int oversample_rate, clk_rate; |
|---|
| 303 | + unsigned int oversample_rate, clk_rate, bclk_parent_rate; |
|---|
| 279 | 304 | int bclk_div, mclk_div; |
|---|
| 280 | 305 | int ret; |
|---|
| 281 | 306 | |
|---|
| .. | .. |
|---|
| 317 | 342 | return -EINVAL; |
|---|
| 318 | 343 | } |
|---|
| 319 | 344 | |
|---|
| 320 | | - bclk_div = sun4i_i2s_get_bclk_div(i2s, i2s->mclk_freq, |
|---|
| 321 | | - rate, word_size); |
|---|
| 345 | + bclk_parent_rate = i2s->variant->get_bclk_parent_rate(i2s); |
|---|
| 346 | + bclk_div = sun4i_i2s_get_bclk_div(i2s, bclk_parent_rate, |
|---|
| 347 | + rate, slots, slot_width); |
|---|
| 322 | 348 | if (bclk_div < 0) { |
|---|
| 323 | 349 | dev_err(dai->dev, "Unsupported BCLK divider: %d\n", bclk_div); |
|---|
| 324 | 350 | return -EINVAL; |
|---|
| 325 | 351 | } |
|---|
| 326 | 352 | |
|---|
| 327 | | - mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate, |
|---|
| 328 | | - clk_rate, rate); |
|---|
| 353 | + mclk_div = sun4i_i2s_get_mclk_div(i2s, clk_rate, i2s->mclk_freq); |
|---|
| 329 | 354 | if (mclk_div < 0) { |
|---|
| 330 | 355 | dev_err(dai->dev, "Unsupported MCLK divider: %d\n", mclk_div); |
|---|
| 331 | 356 | return -EINVAL; |
|---|
| 332 | 357 | } |
|---|
| 333 | | - |
|---|
| 334 | | - /* Adjust the clock division values if needed */ |
|---|
| 335 | | - bclk_div += i2s->variant->bclk_offset; |
|---|
| 336 | | - mclk_div += i2s->variant->mclk_offset; |
|---|
| 337 | 358 | |
|---|
| 338 | 359 | regmap_write(i2s->regmap, SUN4I_I2S_CLK_DIV_REG, |
|---|
| 339 | 360 | SUN4I_I2S_CLK_DIV_BCLK(bclk_div) | |
|---|
| .. | .. |
|---|
| 341 | 362 | |
|---|
| 342 | 363 | regmap_field_write(i2s->field_clkdiv_mclk_en, 1); |
|---|
| 343 | 364 | |
|---|
| 344 | | - /* Set sync period */ |
|---|
| 345 | | - if (i2s->variant->has_fmt_set_lrck_period) |
|---|
| 346 | | - regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG, |
|---|
| 347 | | - SUN8I_I2S_FMT0_LRCK_PERIOD_MASK, |
|---|
| 348 | | - SUN8I_I2S_FMT0_LRCK_PERIOD(32)); |
|---|
| 365 | + return 0; |
|---|
| 366 | +} |
|---|
| 367 | + |
|---|
| 368 | +static s8 sun4i_i2s_get_sr(const struct sun4i_i2s *i2s, int width) |
|---|
| 369 | +{ |
|---|
| 370 | + if (width < 16 || width > 24) |
|---|
| 371 | + return -EINVAL; |
|---|
| 372 | + |
|---|
| 373 | + if (width % 4) |
|---|
| 374 | + return -EINVAL; |
|---|
| 375 | + |
|---|
| 376 | + return (width - 16) / 4; |
|---|
| 377 | +} |
|---|
| 378 | + |
|---|
| 379 | +static s8 sun4i_i2s_get_wss(const struct sun4i_i2s *i2s, int width) |
|---|
| 380 | +{ |
|---|
| 381 | + if (width < 16 || width > 32) |
|---|
| 382 | + return -EINVAL; |
|---|
| 383 | + |
|---|
| 384 | + if (width % 4) |
|---|
| 385 | + return -EINVAL; |
|---|
| 386 | + |
|---|
| 387 | + return (width - 16) / 4; |
|---|
| 388 | +} |
|---|
| 389 | + |
|---|
| 390 | +static s8 sun8i_i2s_get_sr_wss(const struct sun4i_i2s *i2s, int width) |
|---|
| 391 | +{ |
|---|
| 392 | + if (width % 4) |
|---|
| 393 | + return -EINVAL; |
|---|
| 394 | + |
|---|
| 395 | + if (width < 8 || width > 32) |
|---|
| 396 | + return -EINVAL; |
|---|
| 397 | + |
|---|
| 398 | + return (width - 8) / 4 + 1; |
|---|
| 399 | +} |
|---|
| 400 | + |
|---|
| 401 | +static int sun4i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s, |
|---|
| 402 | + const struct snd_pcm_hw_params *params) |
|---|
| 403 | +{ |
|---|
| 404 | + unsigned int channels = params_channels(params); |
|---|
| 405 | + |
|---|
| 406 | + /* Map the channels for playback and capture */ |
|---|
| 407 | + regmap_write(i2s->regmap, SUN4I_I2S_TX_CHAN_MAP_REG, 0x76543210); |
|---|
| 408 | + regmap_write(i2s->regmap, SUN4I_I2S_RX_CHAN_MAP_REG, 0x00003210); |
|---|
| 409 | + |
|---|
| 410 | + /* Configure the channels */ |
|---|
| 411 | + regmap_update_bits(i2s->regmap, SUN4I_I2S_TX_CHAN_SEL_REG, |
|---|
| 412 | + SUN4I_I2S_CHAN_SEL_MASK, |
|---|
| 413 | + SUN4I_I2S_CHAN_SEL(channels)); |
|---|
| 414 | + regmap_update_bits(i2s->regmap, SUN4I_I2S_RX_CHAN_SEL_REG, |
|---|
| 415 | + SUN4I_I2S_CHAN_SEL_MASK, |
|---|
| 416 | + SUN4I_I2S_CHAN_SEL(channels)); |
|---|
| 417 | + |
|---|
| 418 | + return 0; |
|---|
| 419 | +} |
|---|
| 420 | + |
|---|
| 421 | +static int sun8i_i2s_set_chan_cfg(const struct sun4i_i2s *i2s, |
|---|
| 422 | + const struct snd_pcm_hw_params *params) |
|---|
| 423 | +{ |
|---|
| 424 | + unsigned int channels = params_channels(params); |
|---|
| 425 | + unsigned int slots = channels; |
|---|
| 426 | + unsigned int lrck_period; |
|---|
| 427 | + |
|---|
| 428 | + if (i2s->slots) |
|---|
| 429 | + slots = i2s->slots; |
|---|
| 430 | + |
|---|
| 431 | + /* Map the channels for playback and capture */ |
|---|
| 432 | + regmap_write(i2s->regmap, SUN8I_I2S_TX_CHAN_MAP_REG, 0x76543210); |
|---|
| 433 | + regmap_write(i2s->regmap, SUN8I_I2S_RX_CHAN_MAP_REG, 0x76543210); |
|---|
| 434 | + |
|---|
| 435 | + /* Configure the channels */ |
|---|
| 436 | + regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG, |
|---|
| 437 | + SUN4I_I2S_CHAN_SEL_MASK, |
|---|
| 438 | + SUN4I_I2S_CHAN_SEL(channels)); |
|---|
| 439 | + regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG, |
|---|
| 440 | + SUN4I_I2S_CHAN_SEL_MASK, |
|---|
| 441 | + SUN4I_I2S_CHAN_SEL(channels)); |
|---|
| 442 | + |
|---|
| 443 | + regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG, |
|---|
| 444 | + SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK, |
|---|
| 445 | + SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(channels)); |
|---|
| 446 | + regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG, |
|---|
| 447 | + SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK, |
|---|
| 448 | + SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(channels)); |
|---|
| 449 | + |
|---|
| 450 | + switch (i2s->format & SND_SOC_DAIFMT_FORMAT_MASK) { |
|---|
| 451 | + case SND_SOC_DAIFMT_DSP_A: |
|---|
| 452 | + case SND_SOC_DAIFMT_DSP_B: |
|---|
| 453 | + lrck_period = params_physical_width(params) * slots; |
|---|
| 454 | + break; |
|---|
| 455 | + |
|---|
| 456 | + case SND_SOC_DAIFMT_LEFT_J: |
|---|
| 457 | + case SND_SOC_DAIFMT_RIGHT_J: |
|---|
| 458 | + case SND_SOC_DAIFMT_I2S: |
|---|
| 459 | + lrck_period = params_physical_width(params); |
|---|
| 460 | + break; |
|---|
| 461 | + |
|---|
| 462 | + default: |
|---|
| 463 | + return -EINVAL; |
|---|
| 464 | + } |
|---|
| 465 | + |
|---|
| 466 | + regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG, |
|---|
| 467 | + SUN8I_I2S_FMT0_LRCK_PERIOD_MASK, |
|---|
| 468 | + SUN8I_I2S_FMT0_LRCK_PERIOD(lrck_period)); |
|---|
| 469 | + |
|---|
| 470 | + regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG, |
|---|
| 471 | + SUN8I_I2S_TX_CHAN_EN_MASK, |
|---|
| 472 | + SUN8I_I2S_TX_CHAN_EN(channels)); |
|---|
| 349 | 473 | |
|---|
| 350 | 474 | return 0; |
|---|
| 351 | 475 | } |
|---|
| .. | .. |
|---|
| 355 | 479 | struct snd_soc_dai *dai) |
|---|
| 356 | 480 | { |
|---|
| 357 | 481 | struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
|---|
| 358 | | - int sr, wss, channels; |
|---|
| 482 | + unsigned int word_size = params_width(params); |
|---|
| 483 | + unsigned int slot_width = params_physical_width(params); |
|---|
| 484 | + unsigned int channels = params_channels(params); |
|---|
| 485 | + unsigned int slots = channels; |
|---|
| 486 | + int ret, sr, wss; |
|---|
| 359 | 487 | u32 width; |
|---|
| 360 | 488 | |
|---|
| 361 | | - channels = params_channels(params); |
|---|
| 362 | | - if (channels != 2) { |
|---|
| 363 | | - dev_err(dai->dev, "Unsupported number of channels: %d\n", |
|---|
| 364 | | - channels); |
|---|
| 365 | | - return -EINVAL; |
|---|
| 489 | + if (i2s->slots) |
|---|
| 490 | + slots = i2s->slots; |
|---|
| 491 | + |
|---|
| 492 | + if (i2s->slot_width) |
|---|
| 493 | + slot_width = i2s->slot_width; |
|---|
| 494 | + |
|---|
| 495 | + ret = i2s->variant->set_chan_cfg(i2s, params); |
|---|
| 496 | + if (ret < 0) { |
|---|
| 497 | + dev_err(dai->dev, "Invalid channel configuration\n"); |
|---|
| 498 | + return ret; |
|---|
| 366 | 499 | } |
|---|
| 367 | | - |
|---|
| 368 | | - if (i2s->variant->has_chcfg) { |
|---|
| 369 | | - regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG, |
|---|
| 370 | | - SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM_MASK, |
|---|
| 371 | | - SUN8I_I2S_CHAN_CFG_TX_SLOT_NUM(channels)); |
|---|
| 372 | | - regmap_update_bits(i2s->regmap, SUN8I_I2S_CHAN_CFG_REG, |
|---|
| 373 | | - SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM_MASK, |
|---|
| 374 | | - SUN8I_I2S_CHAN_CFG_RX_SLOT_NUM(channels)); |
|---|
| 375 | | - } |
|---|
| 376 | | - |
|---|
| 377 | | - /* Map the channels for playback and capture */ |
|---|
| 378 | | - regmap_field_write(i2s->field_txchanmap, 0x76543210); |
|---|
| 379 | | - regmap_field_write(i2s->field_rxchanmap, 0x00003210); |
|---|
| 380 | | - |
|---|
| 381 | | - /* Configure the channels */ |
|---|
| 382 | | - regmap_field_write(i2s->field_txchansel, |
|---|
| 383 | | - SUN4I_I2S_CHAN_SEL(params_channels(params))); |
|---|
| 384 | | - |
|---|
| 385 | | - regmap_field_write(i2s->field_rxchansel, |
|---|
| 386 | | - SUN4I_I2S_CHAN_SEL(params_channels(params))); |
|---|
| 387 | | - |
|---|
| 388 | | - if (i2s->variant->has_chsel_tx_chen) |
|---|
| 389 | | - regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG, |
|---|
| 390 | | - SUN8I_I2S_TX_CHAN_EN_MASK, |
|---|
| 391 | | - SUN8I_I2S_TX_CHAN_EN(channels)); |
|---|
| 392 | 500 | |
|---|
| 393 | 501 | switch (params_physical_width(params)) { |
|---|
| 394 | 502 | case 16: |
|---|
| .. | .. |
|---|
| 401 | 509 | } |
|---|
| 402 | 510 | i2s->playback_dma_data.addr_width = width; |
|---|
| 403 | 511 | |
|---|
| 404 | | - switch (params_width(params)) { |
|---|
| 405 | | - case 16: |
|---|
| 406 | | - sr = 0; |
|---|
| 407 | | - wss = 0; |
|---|
| 408 | | - break; |
|---|
| 409 | | - |
|---|
| 410 | | - default: |
|---|
| 411 | | - dev_err(dai->dev, "Unsupported sample width: %d\n", |
|---|
| 412 | | - params_width(params)); |
|---|
| 512 | + sr = i2s->variant->get_sr(i2s, word_size); |
|---|
| 513 | + if (sr < 0) |
|---|
| 413 | 514 | return -EINVAL; |
|---|
| 414 | | - } |
|---|
| 415 | 515 | |
|---|
| 416 | | - regmap_field_write(i2s->field_fmt_wss, |
|---|
| 417 | | - wss + i2s->variant->fmt_offset); |
|---|
| 418 | | - regmap_field_write(i2s->field_fmt_sr, |
|---|
| 419 | | - sr + i2s->variant->fmt_offset); |
|---|
| 516 | + wss = i2s->variant->get_wss(i2s, slot_width); |
|---|
| 517 | + if (wss < 0) |
|---|
| 518 | + return -EINVAL; |
|---|
| 519 | + |
|---|
| 520 | + regmap_field_write(i2s->field_fmt_wss, wss); |
|---|
| 521 | + regmap_field_write(i2s->field_fmt_sr, sr); |
|---|
| 420 | 522 | |
|---|
| 421 | 523 | return sun4i_i2s_set_clk_rate(dai, params_rate(params), |
|---|
| 422 | | - params_width(params)); |
|---|
| 524 | + slots, slot_width); |
|---|
| 423 | 525 | } |
|---|
| 424 | 526 | |
|---|
| 425 | | -static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) |
|---|
| 527 | +static int sun4i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s, |
|---|
| 528 | + unsigned int fmt) |
|---|
| 426 | 529 | { |
|---|
| 427 | | - struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
|---|
| 428 | 530 | u32 val; |
|---|
| 429 | | - u32 offset = 0; |
|---|
| 430 | | - u32 bclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL; |
|---|
| 431 | | - u32 lrclk_polarity = SUN4I_I2S_FMT0_POLARITY_NORMAL; |
|---|
| 432 | | - |
|---|
| 433 | | - /* DAI Mode */ |
|---|
| 434 | | - switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
|---|
| 435 | | - case SND_SOC_DAIFMT_I2S: |
|---|
| 436 | | - val = SUN4I_I2S_FMT0_FMT_I2S; |
|---|
| 437 | | - offset = 1; |
|---|
| 438 | | - break; |
|---|
| 439 | | - case SND_SOC_DAIFMT_LEFT_J: |
|---|
| 440 | | - val = SUN4I_I2S_FMT0_FMT_LEFT_J; |
|---|
| 441 | | - break; |
|---|
| 442 | | - case SND_SOC_DAIFMT_RIGHT_J: |
|---|
| 443 | | - val = SUN4I_I2S_FMT0_FMT_RIGHT_J; |
|---|
| 444 | | - break; |
|---|
| 445 | | - default: |
|---|
| 446 | | - dev_err(dai->dev, "Unsupported format: %d\n", |
|---|
| 447 | | - fmt & SND_SOC_DAIFMT_FORMAT_MASK); |
|---|
| 448 | | - return -EINVAL; |
|---|
| 449 | | - } |
|---|
| 450 | | - |
|---|
| 451 | | - if (i2s->variant->has_chsel_offset) { |
|---|
| 452 | | - /* |
|---|
| 453 | | - * offset being set indicates that we're connected to an i2s |
|---|
| 454 | | - * device, however offset is only used on the sun8i block and |
|---|
| 455 | | - * i2s shares the same setting with the LJ format. Increment |
|---|
| 456 | | - * val so that the bit to value to write is correct. |
|---|
| 457 | | - */ |
|---|
| 458 | | - if (offset > 0) |
|---|
| 459 | | - val++; |
|---|
| 460 | | - /* blck offset determines whether i2s or LJ */ |
|---|
| 461 | | - regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG, |
|---|
| 462 | | - SUN8I_I2S_TX_CHAN_OFFSET_MASK, |
|---|
| 463 | | - SUN8I_I2S_TX_CHAN_OFFSET(offset)); |
|---|
| 464 | | - |
|---|
| 465 | | - regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG, |
|---|
| 466 | | - SUN8I_I2S_TX_CHAN_OFFSET_MASK, |
|---|
| 467 | | - SUN8I_I2S_TX_CHAN_OFFSET(offset)); |
|---|
| 468 | | - } |
|---|
| 469 | | - |
|---|
| 470 | | - regmap_field_write(i2s->field_fmt_mode, val); |
|---|
| 471 | 531 | |
|---|
| 472 | 532 | /* DAI clock polarity */ |
|---|
| 473 | 533 | switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
|---|
| 474 | 534 | case SND_SOC_DAIFMT_IB_IF: |
|---|
| 475 | 535 | /* Invert both clocks */ |
|---|
| 476 | | - bclk_polarity = SUN4I_I2S_FMT0_POLARITY_INVERTED; |
|---|
| 477 | | - lrclk_polarity = SUN4I_I2S_FMT0_POLARITY_INVERTED; |
|---|
| 536 | + val = SUN4I_I2S_FMT0_BCLK_POLARITY_INVERTED | |
|---|
| 537 | + SUN4I_I2S_FMT0_LRCLK_POLARITY_INVERTED; |
|---|
| 478 | 538 | break; |
|---|
| 479 | 539 | case SND_SOC_DAIFMT_IB_NF: |
|---|
| 480 | 540 | /* Invert bit clock */ |
|---|
| 481 | | - bclk_polarity = SUN4I_I2S_FMT0_POLARITY_INVERTED; |
|---|
| 541 | + val = SUN4I_I2S_FMT0_BCLK_POLARITY_INVERTED; |
|---|
| 482 | 542 | break; |
|---|
| 483 | 543 | case SND_SOC_DAIFMT_NB_IF: |
|---|
| 484 | 544 | /* Invert frame clock */ |
|---|
| 485 | | - lrclk_polarity = SUN4I_I2S_FMT0_POLARITY_INVERTED; |
|---|
| 545 | + val = SUN4I_I2S_FMT0_LRCLK_POLARITY_INVERTED; |
|---|
| 486 | 546 | break; |
|---|
| 487 | 547 | case SND_SOC_DAIFMT_NB_NF: |
|---|
| 548 | + val = 0; |
|---|
| 488 | 549 | break; |
|---|
| 489 | 550 | default: |
|---|
| 490 | | - dev_err(dai->dev, "Unsupported clock polarity: %d\n", |
|---|
| 491 | | - fmt & SND_SOC_DAIFMT_INV_MASK); |
|---|
| 492 | 551 | return -EINVAL; |
|---|
| 493 | 552 | } |
|---|
| 494 | 553 | |
|---|
| 495 | | - regmap_field_write(i2s->field_fmt_bclk, bclk_polarity); |
|---|
| 496 | | - regmap_field_write(i2s->field_fmt_lrclk, lrclk_polarity); |
|---|
| 554 | + regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG, |
|---|
| 555 | + SUN4I_I2S_FMT0_LRCLK_POLARITY_MASK | |
|---|
| 556 | + SUN4I_I2S_FMT0_BCLK_POLARITY_MASK, |
|---|
| 557 | + val); |
|---|
| 497 | 558 | |
|---|
| 498 | | - if (i2s->variant->has_slave_select_bit) { |
|---|
| 499 | | - /* DAI clock master masks */ |
|---|
| 500 | | - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
|---|
| 501 | | - case SND_SOC_DAIFMT_CBS_CFS: |
|---|
| 502 | | - /* BCLK and LRCLK master */ |
|---|
| 503 | | - val = SUN4I_I2S_CTRL_MODE_MASTER; |
|---|
| 504 | | - break; |
|---|
| 505 | | - case SND_SOC_DAIFMT_CBM_CFM: |
|---|
| 506 | | - /* BCLK and LRCLK slave */ |
|---|
| 507 | | - val = SUN4I_I2S_CTRL_MODE_SLAVE; |
|---|
| 508 | | - break; |
|---|
| 509 | | - default: |
|---|
| 510 | | - dev_err(dai->dev, "Unsupported slave setting: %d\n", |
|---|
| 511 | | - fmt & SND_SOC_DAIFMT_MASTER_MASK); |
|---|
| 512 | | - return -EINVAL; |
|---|
| 513 | | - } |
|---|
| 514 | | - regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, |
|---|
| 515 | | - SUN4I_I2S_CTRL_MODE_MASK, |
|---|
| 516 | | - val); |
|---|
| 517 | | - } else { |
|---|
| 518 | | - /* |
|---|
| 519 | | - * The newer i2s block does not have a slave select bit, |
|---|
| 520 | | - * instead the clk pins are configured as inputs. |
|---|
| 521 | | - */ |
|---|
| 522 | | - /* DAI clock master masks */ |
|---|
| 523 | | - switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
|---|
| 524 | | - case SND_SOC_DAIFMT_CBS_CFS: |
|---|
| 525 | | - /* BCLK and LRCLK master */ |
|---|
| 526 | | - val = SUN8I_I2S_CTRL_BCLK_OUT | |
|---|
| 527 | | - SUN8I_I2S_CTRL_LRCK_OUT; |
|---|
| 528 | | - break; |
|---|
| 529 | | - case SND_SOC_DAIFMT_CBM_CFM: |
|---|
| 530 | | - /* BCLK and LRCLK slave */ |
|---|
| 531 | | - val = 0; |
|---|
| 532 | | - break; |
|---|
| 533 | | - default: |
|---|
| 534 | | - dev_err(dai->dev, "Unsupported slave setting: %d\n", |
|---|
| 535 | | - fmt & SND_SOC_DAIFMT_MASTER_MASK); |
|---|
| 536 | | - return -EINVAL; |
|---|
| 537 | | - } |
|---|
| 538 | | - regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, |
|---|
| 539 | | - SUN8I_I2S_CTRL_BCLK_OUT | |
|---|
| 540 | | - SUN8I_I2S_CTRL_LRCK_OUT, |
|---|
| 541 | | - val); |
|---|
| 559 | + /* DAI Mode */ |
|---|
| 560 | + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
|---|
| 561 | + case SND_SOC_DAIFMT_I2S: |
|---|
| 562 | + val = SUN4I_I2S_FMT0_FMT_I2S; |
|---|
| 563 | + break; |
|---|
| 564 | + |
|---|
| 565 | + case SND_SOC_DAIFMT_LEFT_J: |
|---|
| 566 | + val = SUN4I_I2S_FMT0_FMT_LEFT_J; |
|---|
| 567 | + break; |
|---|
| 568 | + |
|---|
| 569 | + case SND_SOC_DAIFMT_RIGHT_J: |
|---|
| 570 | + val = SUN4I_I2S_FMT0_FMT_RIGHT_J; |
|---|
| 571 | + break; |
|---|
| 572 | + |
|---|
| 573 | + default: |
|---|
| 574 | + return -EINVAL; |
|---|
| 575 | + } |
|---|
| 576 | + |
|---|
| 577 | + regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG, |
|---|
| 578 | + SUN4I_I2S_FMT0_FMT_MASK, val); |
|---|
| 579 | + |
|---|
| 580 | + /* DAI clock master masks */ |
|---|
| 581 | + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
|---|
| 582 | + case SND_SOC_DAIFMT_CBS_CFS: |
|---|
| 583 | + /* BCLK and LRCLK master */ |
|---|
| 584 | + val = SUN4I_I2S_CTRL_MODE_MASTER; |
|---|
| 585 | + break; |
|---|
| 586 | + |
|---|
| 587 | + case SND_SOC_DAIFMT_CBM_CFM: |
|---|
| 588 | + /* BCLK and LRCLK slave */ |
|---|
| 589 | + val = SUN4I_I2S_CTRL_MODE_SLAVE; |
|---|
| 590 | + break; |
|---|
| 591 | + |
|---|
| 592 | + default: |
|---|
| 593 | + return -EINVAL; |
|---|
| 594 | + } |
|---|
| 595 | + regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, |
|---|
| 596 | + SUN4I_I2S_CTRL_MODE_MASK, val); |
|---|
| 597 | + return 0; |
|---|
| 598 | +} |
|---|
| 599 | + |
|---|
| 600 | +static int sun8i_i2s_set_soc_fmt(const struct sun4i_i2s *i2s, |
|---|
| 601 | + unsigned int fmt) |
|---|
| 602 | +{ |
|---|
| 603 | + u32 mode, val; |
|---|
| 604 | + u8 offset; |
|---|
| 605 | + |
|---|
| 606 | + /* |
|---|
| 607 | + * DAI clock polarity |
|---|
| 608 | + * |
|---|
| 609 | + * The setup for LRCK contradicts the datasheet, but under a |
|---|
| 610 | + * scope it's clear that the LRCK polarity is reversed |
|---|
| 611 | + * compared to the expected polarity on the bus. |
|---|
| 612 | + */ |
|---|
| 613 | + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { |
|---|
| 614 | + case SND_SOC_DAIFMT_IB_IF: |
|---|
| 615 | + /* Invert both clocks */ |
|---|
| 616 | + val = SUN8I_I2S_FMT0_BCLK_POLARITY_INVERTED; |
|---|
| 617 | + break; |
|---|
| 618 | + case SND_SOC_DAIFMT_IB_NF: |
|---|
| 619 | + /* Invert bit clock */ |
|---|
| 620 | + val = SUN8I_I2S_FMT0_BCLK_POLARITY_INVERTED | |
|---|
| 621 | + SUN8I_I2S_FMT0_LRCLK_POLARITY_INVERTED; |
|---|
| 622 | + break; |
|---|
| 623 | + case SND_SOC_DAIFMT_NB_IF: |
|---|
| 624 | + /* Invert frame clock */ |
|---|
| 625 | + val = 0; |
|---|
| 626 | + break; |
|---|
| 627 | + case SND_SOC_DAIFMT_NB_NF: |
|---|
| 628 | + val = SUN8I_I2S_FMT0_LRCLK_POLARITY_INVERTED; |
|---|
| 629 | + break; |
|---|
| 630 | + default: |
|---|
| 631 | + return -EINVAL; |
|---|
| 632 | + } |
|---|
| 633 | + |
|---|
| 634 | + regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG, |
|---|
| 635 | + SUN8I_I2S_FMT0_LRCLK_POLARITY_MASK | |
|---|
| 636 | + SUN8I_I2S_FMT0_BCLK_POLARITY_MASK, |
|---|
| 637 | + val); |
|---|
| 638 | + |
|---|
| 639 | + /* DAI Mode */ |
|---|
| 640 | + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { |
|---|
| 641 | + case SND_SOC_DAIFMT_DSP_A: |
|---|
| 642 | + mode = SUN8I_I2S_CTRL_MODE_PCM; |
|---|
| 643 | + offset = 1; |
|---|
| 644 | + break; |
|---|
| 645 | + |
|---|
| 646 | + case SND_SOC_DAIFMT_DSP_B: |
|---|
| 647 | + mode = SUN8I_I2S_CTRL_MODE_PCM; |
|---|
| 648 | + offset = 0; |
|---|
| 649 | + break; |
|---|
| 650 | + |
|---|
| 651 | + case SND_SOC_DAIFMT_I2S: |
|---|
| 652 | + mode = SUN8I_I2S_CTRL_MODE_LEFT; |
|---|
| 653 | + offset = 1; |
|---|
| 654 | + break; |
|---|
| 655 | + |
|---|
| 656 | + case SND_SOC_DAIFMT_LEFT_J: |
|---|
| 657 | + mode = SUN8I_I2S_CTRL_MODE_LEFT; |
|---|
| 658 | + offset = 0; |
|---|
| 659 | + break; |
|---|
| 660 | + |
|---|
| 661 | + case SND_SOC_DAIFMT_RIGHT_J: |
|---|
| 662 | + mode = SUN8I_I2S_CTRL_MODE_RIGHT; |
|---|
| 663 | + offset = 0; |
|---|
| 664 | + break; |
|---|
| 665 | + |
|---|
| 666 | + default: |
|---|
| 667 | + return -EINVAL; |
|---|
| 668 | + } |
|---|
| 669 | + |
|---|
| 670 | + regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, |
|---|
| 671 | + SUN8I_I2S_CTRL_MODE_MASK, mode); |
|---|
| 672 | + regmap_update_bits(i2s->regmap, SUN8I_I2S_TX_CHAN_SEL_REG, |
|---|
| 673 | + SUN8I_I2S_TX_CHAN_OFFSET_MASK, |
|---|
| 674 | + SUN8I_I2S_TX_CHAN_OFFSET(offset)); |
|---|
| 675 | + regmap_update_bits(i2s->regmap, SUN8I_I2S_RX_CHAN_SEL_REG, |
|---|
| 676 | + SUN8I_I2S_TX_CHAN_OFFSET_MASK, |
|---|
| 677 | + SUN8I_I2S_TX_CHAN_OFFSET(offset)); |
|---|
| 678 | + |
|---|
| 679 | + /* DAI clock master masks */ |
|---|
| 680 | + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { |
|---|
| 681 | + case SND_SOC_DAIFMT_CBS_CFS: |
|---|
| 682 | + /* BCLK and LRCLK master */ |
|---|
| 683 | + val = SUN8I_I2S_CTRL_BCLK_OUT | SUN8I_I2S_CTRL_LRCK_OUT; |
|---|
| 684 | + break; |
|---|
| 685 | + |
|---|
| 686 | + case SND_SOC_DAIFMT_CBM_CFM: |
|---|
| 687 | + /* BCLK and LRCLK slave */ |
|---|
| 688 | + val = 0; |
|---|
| 689 | + break; |
|---|
| 690 | + |
|---|
| 691 | + default: |
|---|
| 692 | + return -EINVAL; |
|---|
| 693 | + } |
|---|
| 694 | + |
|---|
| 695 | + regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, |
|---|
| 696 | + SUN8I_I2S_CTRL_BCLK_OUT | SUN8I_I2S_CTRL_LRCK_OUT, |
|---|
| 697 | + val); |
|---|
| 698 | + |
|---|
| 699 | + return 0; |
|---|
| 700 | +} |
|---|
| 701 | + |
|---|
| 702 | +static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) |
|---|
| 703 | +{ |
|---|
| 704 | + struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
|---|
| 705 | + int ret; |
|---|
| 706 | + |
|---|
| 707 | + ret = i2s->variant->set_fmt(i2s, fmt); |
|---|
| 708 | + if (ret) { |
|---|
| 709 | + dev_err(dai->dev, "Unsupported format configuration\n"); |
|---|
| 710 | + return ret; |
|---|
| 542 | 711 | } |
|---|
| 543 | 712 | |
|---|
| 544 | 713 | /* Set significant bits in our FIFOs */ |
|---|
| .. | .. |
|---|
| 547 | 716 | SUN4I_I2S_FIFO_CTRL_RX_MODE_MASK, |
|---|
| 548 | 717 | SUN4I_I2S_FIFO_CTRL_TX_MODE(1) | |
|---|
| 549 | 718 | SUN4I_I2S_FIFO_CTRL_RX_MODE(1)); |
|---|
| 719 | + |
|---|
| 720 | + i2s->format = fmt; |
|---|
| 721 | + |
|---|
| 550 | 722 | return 0; |
|---|
| 551 | 723 | } |
|---|
| 552 | 724 | |
|---|
| .. | .. |
|---|
| 649 | 821 | return 0; |
|---|
| 650 | 822 | } |
|---|
| 651 | 823 | |
|---|
| 652 | | -static int sun4i_i2s_startup(struct snd_pcm_substream *substream, |
|---|
| 653 | | - struct snd_soc_dai *dai) |
|---|
| 654 | | -{ |
|---|
| 655 | | - struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
|---|
| 656 | | - |
|---|
| 657 | | - /* Enable the whole hardware block */ |
|---|
| 658 | | - regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, |
|---|
| 659 | | - SUN4I_I2S_CTRL_GL_EN, SUN4I_I2S_CTRL_GL_EN); |
|---|
| 660 | | - |
|---|
| 661 | | - /* Enable the first output line */ |
|---|
| 662 | | - regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, |
|---|
| 663 | | - SUN4I_I2S_CTRL_SDO_EN_MASK, |
|---|
| 664 | | - SUN4I_I2S_CTRL_SDO_EN(0)); |
|---|
| 665 | | - |
|---|
| 666 | | - |
|---|
| 667 | | - return clk_prepare_enable(i2s->mod_clk); |
|---|
| 668 | | -} |
|---|
| 669 | | - |
|---|
| 670 | | -static void sun4i_i2s_shutdown(struct snd_pcm_substream *substream, |
|---|
| 671 | | - struct snd_soc_dai *dai) |
|---|
| 672 | | -{ |
|---|
| 673 | | - struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
|---|
| 674 | | - |
|---|
| 675 | | - clk_disable_unprepare(i2s->mod_clk); |
|---|
| 676 | | - |
|---|
| 677 | | - /* Disable our output lines */ |
|---|
| 678 | | - regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, |
|---|
| 679 | | - SUN4I_I2S_CTRL_SDO_EN_MASK, 0); |
|---|
| 680 | | - |
|---|
| 681 | | - /* Disable the whole hardware block */ |
|---|
| 682 | | - regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, |
|---|
| 683 | | - SUN4I_I2S_CTRL_GL_EN, 0); |
|---|
| 684 | | -} |
|---|
| 685 | | - |
|---|
| 686 | 824 | static int sun4i_i2s_set_sysclk(struct snd_soc_dai *dai, int clk_id, |
|---|
| 687 | 825 | unsigned int freq, int dir) |
|---|
| 688 | 826 | { |
|---|
| .. | .. |
|---|
| 696 | 834 | return 0; |
|---|
| 697 | 835 | } |
|---|
| 698 | 836 | |
|---|
| 837 | +static int sun4i_i2s_set_tdm_slot(struct snd_soc_dai *dai, |
|---|
| 838 | + unsigned int tx_mask, unsigned int rx_mask, |
|---|
| 839 | + int slots, int slot_width) |
|---|
| 840 | +{ |
|---|
| 841 | + struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai); |
|---|
| 842 | + |
|---|
| 843 | + if (slots > 8) |
|---|
| 844 | + return -EINVAL; |
|---|
| 845 | + |
|---|
| 846 | + i2s->slots = slots; |
|---|
| 847 | + i2s->slot_width = slot_width; |
|---|
| 848 | + |
|---|
| 849 | + return 0; |
|---|
| 850 | +} |
|---|
| 851 | + |
|---|
| 699 | 852 | static const struct snd_soc_dai_ops sun4i_i2s_dai_ops = { |
|---|
| 700 | 853 | .hw_params = sun4i_i2s_hw_params, |
|---|
| 701 | 854 | .set_fmt = sun4i_i2s_set_fmt, |
|---|
| 702 | 855 | .set_sysclk = sun4i_i2s_set_sysclk, |
|---|
| 703 | | - .shutdown = sun4i_i2s_shutdown, |
|---|
| 704 | | - .startup = sun4i_i2s_startup, |
|---|
| 856 | + .set_tdm_slot = sun4i_i2s_set_tdm_slot, |
|---|
| 705 | 857 | .trigger = sun4i_i2s_trigger, |
|---|
| 706 | 858 | }; |
|---|
| 707 | 859 | |
|---|
| .. | .. |
|---|
| 722 | 874 | .probe = sun4i_i2s_dai_probe, |
|---|
| 723 | 875 | .capture = { |
|---|
| 724 | 876 | .stream_name = "Capture", |
|---|
| 725 | | - .channels_min = 2, |
|---|
| 726 | | - .channels_max = 2, |
|---|
| 877 | + .channels_min = 1, |
|---|
| 878 | + .channels_max = 8, |
|---|
| 727 | 879 | .rates = SNDRV_PCM_RATE_8000_192000, |
|---|
| 728 | 880 | .formats = SNDRV_PCM_FMTBIT_S16_LE, |
|---|
| 729 | 881 | }, |
|---|
| 730 | 882 | .playback = { |
|---|
| 731 | 883 | .stream_name = "Playback", |
|---|
| 732 | | - .channels_min = 2, |
|---|
| 733 | | - .channels_max = 2, |
|---|
| 884 | + .channels_min = 1, |
|---|
| 885 | + .channels_max = 8, |
|---|
| 734 | 886 | .rates = SNDRV_PCM_RATE_8000_192000, |
|---|
| 735 | 887 | .formats = SNDRV_PCM_FMTBIT_S16_LE, |
|---|
| 736 | 888 | }, |
|---|
| .. | .. |
|---|
| 874 | 1026 | goto err_disable_clk; |
|---|
| 875 | 1027 | } |
|---|
| 876 | 1028 | |
|---|
| 1029 | + /* Enable the whole hardware block */ |
|---|
| 1030 | + regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, |
|---|
| 1031 | + SUN4I_I2S_CTRL_GL_EN, SUN4I_I2S_CTRL_GL_EN); |
|---|
| 1032 | + |
|---|
| 1033 | + /* Enable the first output line */ |
|---|
| 1034 | + regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, |
|---|
| 1035 | + SUN4I_I2S_CTRL_SDO_EN_MASK, |
|---|
| 1036 | + SUN4I_I2S_CTRL_SDO_EN(0)); |
|---|
| 1037 | + |
|---|
| 1038 | + ret = clk_prepare_enable(i2s->mod_clk); |
|---|
| 1039 | + if (ret) { |
|---|
| 1040 | + dev_err(dev, "Failed to enable module clock\n"); |
|---|
| 1041 | + goto err_disable_clk; |
|---|
| 1042 | + } |
|---|
| 1043 | + |
|---|
| 877 | 1044 | return 0; |
|---|
| 878 | 1045 | |
|---|
| 879 | 1046 | err_disable_clk: |
|---|
| .. | .. |
|---|
| 884 | 1051 | static int sun4i_i2s_runtime_suspend(struct device *dev) |
|---|
| 885 | 1052 | { |
|---|
| 886 | 1053 | struct sun4i_i2s *i2s = dev_get_drvdata(dev); |
|---|
| 1054 | + |
|---|
| 1055 | + clk_disable_unprepare(i2s->mod_clk); |
|---|
| 1056 | + |
|---|
| 1057 | + /* Disable our output lines */ |
|---|
| 1058 | + regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, |
|---|
| 1059 | + SUN4I_I2S_CTRL_SDO_EN_MASK, 0); |
|---|
| 1060 | + |
|---|
| 1061 | + /* Disable the whole hardware block */ |
|---|
| 1062 | + regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG, |
|---|
| 1063 | + SUN4I_I2S_CTRL_GL_EN, 0); |
|---|
| 887 | 1064 | |
|---|
| 888 | 1065 | regcache_cache_only(i2s->regmap, true); |
|---|
| 889 | 1066 | |
|---|
| .. | .. |
|---|
| 899 | 1076 | .field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 7, 7), |
|---|
| 900 | 1077 | .field_fmt_wss = REG_FIELD(SUN4I_I2S_FMT0_REG, 2, 3), |
|---|
| 901 | 1078 | .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5), |
|---|
| 902 | | - .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6), |
|---|
| 903 | | - .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7), |
|---|
| 904 | | - .has_slave_select_bit = true, |
|---|
| 905 | | - .field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1), |
|---|
| 906 | | - .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31), |
|---|
| 907 | | - .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31), |
|---|
| 908 | | - .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2), |
|---|
| 909 | | - .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2), |
|---|
| 1079 | + .bclk_dividers = sun4i_i2s_bclk_div, |
|---|
| 1080 | + .num_bclk_dividers = ARRAY_SIZE(sun4i_i2s_bclk_div), |
|---|
| 1081 | + .mclk_dividers = sun4i_i2s_mclk_div, |
|---|
| 1082 | + .num_mclk_dividers = ARRAY_SIZE(sun4i_i2s_mclk_div), |
|---|
| 1083 | + .get_bclk_parent_rate = sun4i_i2s_get_bclk_parent_rate, |
|---|
| 1084 | + .get_sr = sun4i_i2s_get_sr, |
|---|
| 1085 | + .get_wss = sun4i_i2s_get_wss, |
|---|
| 1086 | + .set_chan_cfg = sun4i_i2s_set_chan_cfg, |
|---|
| 1087 | + .set_fmt = sun4i_i2s_set_soc_fmt, |
|---|
| 910 | 1088 | }; |
|---|
| 911 | 1089 | |
|---|
| 912 | 1090 | static const struct sun4i_i2s_quirks sun6i_a31_i2s_quirks = { |
|---|
| .. | .. |
|---|
| 916 | 1094 | .field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 7, 7), |
|---|
| 917 | 1095 | .field_fmt_wss = REG_FIELD(SUN4I_I2S_FMT0_REG, 2, 3), |
|---|
| 918 | 1096 | .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5), |
|---|
| 919 | | - .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6), |
|---|
| 920 | | - .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7), |
|---|
| 921 | | - .has_slave_select_bit = true, |
|---|
| 922 | | - .field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1), |
|---|
| 923 | | - .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31), |
|---|
| 924 | | - .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31), |
|---|
| 925 | | - .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2), |
|---|
| 926 | | - .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2), |
|---|
| 1097 | + .bclk_dividers = sun4i_i2s_bclk_div, |
|---|
| 1098 | + .num_bclk_dividers = ARRAY_SIZE(sun4i_i2s_bclk_div), |
|---|
| 1099 | + .mclk_dividers = sun4i_i2s_mclk_div, |
|---|
| 1100 | + .num_mclk_dividers = ARRAY_SIZE(sun4i_i2s_mclk_div), |
|---|
| 1101 | + .get_bclk_parent_rate = sun4i_i2s_get_bclk_parent_rate, |
|---|
| 1102 | + .get_sr = sun4i_i2s_get_sr, |
|---|
| 1103 | + .get_wss = sun4i_i2s_get_wss, |
|---|
| 1104 | + .set_chan_cfg = sun4i_i2s_set_chan_cfg, |
|---|
| 1105 | + .set_fmt = sun4i_i2s_set_soc_fmt, |
|---|
| 927 | 1106 | }; |
|---|
| 928 | 1107 | |
|---|
| 1108 | +/* |
|---|
| 1109 | + * This doesn't describe the TDM controller documented in the A83t |
|---|
| 1110 | + * datasheet, but the three undocumented I2S controller that use the |
|---|
| 1111 | + * older design. |
|---|
| 1112 | + */ |
|---|
| 929 | 1113 | static const struct sun4i_i2s_quirks sun8i_a83t_i2s_quirks = { |
|---|
| 930 | 1114 | .has_reset = true, |
|---|
| 931 | 1115 | .reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG, |
|---|
| .. | .. |
|---|
| 933 | 1117 | .field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 7, 7), |
|---|
| 934 | 1118 | .field_fmt_wss = REG_FIELD(SUN4I_I2S_FMT0_REG, 2, 3), |
|---|
| 935 | 1119 | .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5), |
|---|
| 936 | | - .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 6, 6), |
|---|
| 937 | | - .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7), |
|---|
| 938 | | - .has_slave_select_bit = true, |
|---|
| 939 | | - .field_fmt_mode = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 1), |
|---|
| 940 | | - .field_txchanmap = REG_FIELD(SUN4I_I2S_TX_CHAN_MAP_REG, 0, 31), |
|---|
| 941 | | - .field_rxchanmap = REG_FIELD(SUN4I_I2S_RX_CHAN_MAP_REG, 0, 31), |
|---|
| 942 | | - .field_txchansel = REG_FIELD(SUN4I_I2S_TX_CHAN_SEL_REG, 0, 2), |
|---|
| 943 | | - .field_rxchansel = REG_FIELD(SUN4I_I2S_RX_CHAN_SEL_REG, 0, 2), |
|---|
| 1120 | + .bclk_dividers = sun4i_i2s_bclk_div, |
|---|
| 1121 | + .num_bclk_dividers = ARRAY_SIZE(sun4i_i2s_bclk_div), |
|---|
| 1122 | + .mclk_dividers = sun4i_i2s_mclk_div, |
|---|
| 1123 | + .num_mclk_dividers = ARRAY_SIZE(sun4i_i2s_mclk_div), |
|---|
| 1124 | + .get_bclk_parent_rate = sun4i_i2s_get_bclk_parent_rate, |
|---|
| 1125 | + .get_sr = sun4i_i2s_get_sr, |
|---|
| 1126 | + .get_wss = sun4i_i2s_get_wss, |
|---|
| 1127 | + .set_chan_cfg = sun4i_i2s_set_chan_cfg, |
|---|
| 1128 | + .set_fmt = sun4i_i2s_set_soc_fmt, |
|---|
| 944 | 1129 | }; |
|---|
| 945 | 1130 | |
|---|
| 946 | 1131 | static const struct sun4i_i2s_quirks sun8i_h3_i2s_quirks = { |
|---|
| 947 | 1132 | .has_reset = true, |
|---|
| 948 | 1133 | .reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG, |
|---|
| 949 | 1134 | .sun4i_i2s_regmap = &sun8i_i2s_regmap_config, |
|---|
| 950 | | - .mclk_offset = 1, |
|---|
| 951 | | - .bclk_offset = 2, |
|---|
| 952 | | - .fmt_offset = 3, |
|---|
| 953 | | - .has_fmt_set_lrck_period = true, |
|---|
| 954 | | - .has_chcfg = true, |
|---|
| 955 | | - .has_chsel_tx_chen = true, |
|---|
| 956 | | - .has_chsel_offset = true, |
|---|
| 957 | 1135 | .field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 8, 8), |
|---|
| 958 | 1136 | .field_fmt_wss = REG_FIELD(SUN4I_I2S_FMT0_REG, 0, 2), |
|---|
| 959 | 1137 | .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 6), |
|---|
| 960 | | - .field_fmt_bclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 7, 7), |
|---|
| 961 | | - .field_fmt_lrclk = REG_FIELD(SUN4I_I2S_FMT0_REG, 19, 19), |
|---|
| 962 | | - .field_fmt_mode = REG_FIELD(SUN4I_I2S_CTRL_REG, 4, 5), |
|---|
| 963 | | - .field_txchanmap = REG_FIELD(SUN8I_I2S_TX_CHAN_MAP_REG, 0, 31), |
|---|
| 964 | | - .field_rxchanmap = REG_FIELD(SUN8I_I2S_RX_CHAN_MAP_REG, 0, 31), |
|---|
| 965 | | - .field_txchansel = REG_FIELD(SUN8I_I2S_TX_CHAN_SEL_REG, 0, 2), |
|---|
| 966 | | - .field_rxchansel = REG_FIELD(SUN8I_I2S_RX_CHAN_SEL_REG, 0, 2), |
|---|
| 1138 | + .bclk_dividers = sun8i_i2s_clk_div, |
|---|
| 1139 | + .num_bclk_dividers = ARRAY_SIZE(sun8i_i2s_clk_div), |
|---|
| 1140 | + .mclk_dividers = sun8i_i2s_clk_div, |
|---|
| 1141 | + .num_mclk_dividers = ARRAY_SIZE(sun8i_i2s_clk_div), |
|---|
| 1142 | + .get_bclk_parent_rate = sun8i_i2s_get_bclk_parent_rate, |
|---|
| 1143 | + .get_sr = sun8i_i2s_get_sr_wss, |
|---|
| 1144 | + .get_wss = sun8i_i2s_get_sr_wss, |
|---|
| 1145 | + .set_chan_cfg = sun8i_i2s_set_chan_cfg, |
|---|
| 1146 | + .set_fmt = sun8i_i2s_set_soc_fmt, |
|---|
| 1147 | +}; |
|---|
| 1148 | + |
|---|
| 1149 | +static const struct sun4i_i2s_quirks sun50i_a64_codec_i2s_quirks = { |
|---|
| 1150 | + .has_reset = true, |
|---|
| 1151 | + .reg_offset_txdata = SUN8I_I2S_FIFO_TX_REG, |
|---|
| 1152 | + .sun4i_i2s_regmap = &sun4i_i2s_regmap_config, |
|---|
| 1153 | + .field_clkdiv_mclk_en = REG_FIELD(SUN4I_I2S_CLK_DIV_REG, 7, 7), |
|---|
| 1154 | + .field_fmt_wss = REG_FIELD(SUN4I_I2S_FMT0_REG, 2, 3), |
|---|
| 1155 | + .field_fmt_sr = REG_FIELD(SUN4I_I2S_FMT0_REG, 4, 5), |
|---|
| 1156 | + .bclk_dividers = sun4i_i2s_bclk_div, |
|---|
| 1157 | + .num_bclk_dividers = ARRAY_SIZE(sun4i_i2s_bclk_div), |
|---|
| 1158 | + .mclk_dividers = sun4i_i2s_mclk_div, |
|---|
| 1159 | + .num_mclk_dividers = ARRAY_SIZE(sun4i_i2s_mclk_div), |
|---|
| 1160 | + .get_bclk_parent_rate = sun4i_i2s_get_bclk_parent_rate, |
|---|
| 1161 | + .get_sr = sun4i_i2s_get_sr, |
|---|
| 1162 | + .get_wss = sun4i_i2s_get_wss, |
|---|
| 1163 | + .set_chan_cfg = sun4i_i2s_set_chan_cfg, |
|---|
| 1164 | + .set_fmt = sun4i_i2s_set_soc_fmt, |
|---|
| 967 | 1165 | }; |
|---|
| 968 | 1166 | |
|---|
| 969 | 1167 | static int sun4i_i2s_init_regmap_fields(struct device *dev, |
|---|
| .. | .. |
|---|
| 987 | 1185 | if (IS_ERR(i2s->field_fmt_sr)) |
|---|
| 988 | 1186 | return PTR_ERR(i2s->field_fmt_sr); |
|---|
| 989 | 1187 | |
|---|
| 990 | | - i2s->field_fmt_bclk = |
|---|
| 991 | | - devm_regmap_field_alloc(dev, i2s->regmap, |
|---|
| 992 | | - i2s->variant->field_fmt_bclk); |
|---|
| 993 | | - if (IS_ERR(i2s->field_fmt_bclk)) |
|---|
| 994 | | - return PTR_ERR(i2s->field_fmt_bclk); |
|---|
| 995 | | - |
|---|
| 996 | | - i2s->field_fmt_lrclk = |
|---|
| 997 | | - devm_regmap_field_alloc(dev, i2s->regmap, |
|---|
| 998 | | - i2s->variant->field_fmt_lrclk); |
|---|
| 999 | | - if (IS_ERR(i2s->field_fmt_lrclk)) |
|---|
| 1000 | | - return PTR_ERR(i2s->field_fmt_lrclk); |
|---|
| 1001 | | - |
|---|
| 1002 | | - i2s->field_fmt_mode = |
|---|
| 1003 | | - devm_regmap_field_alloc(dev, i2s->regmap, |
|---|
| 1004 | | - i2s->variant->field_fmt_mode); |
|---|
| 1005 | | - if (IS_ERR(i2s->field_fmt_mode)) |
|---|
| 1006 | | - return PTR_ERR(i2s->field_fmt_mode); |
|---|
| 1007 | | - |
|---|
| 1008 | | - i2s->field_txchanmap = |
|---|
| 1009 | | - devm_regmap_field_alloc(dev, i2s->regmap, |
|---|
| 1010 | | - i2s->variant->field_txchanmap); |
|---|
| 1011 | | - if (IS_ERR(i2s->field_txchanmap)) |
|---|
| 1012 | | - return PTR_ERR(i2s->field_txchanmap); |
|---|
| 1013 | | - |
|---|
| 1014 | | - i2s->field_rxchanmap = |
|---|
| 1015 | | - devm_regmap_field_alloc(dev, i2s->regmap, |
|---|
| 1016 | | - i2s->variant->field_rxchanmap); |
|---|
| 1017 | | - if (IS_ERR(i2s->field_rxchanmap)) |
|---|
| 1018 | | - return PTR_ERR(i2s->field_rxchanmap); |
|---|
| 1019 | | - |
|---|
| 1020 | | - i2s->field_txchansel = |
|---|
| 1021 | | - devm_regmap_field_alloc(dev, i2s->regmap, |
|---|
| 1022 | | - i2s->variant->field_txchansel); |
|---|
| 1023 | | - if (IS_ERR(i2s->field_txchansel)) |
|---|
| 1024 | | - return PTR_ERR(i2s->field_txchansel); |
|---|
| 1025 | | - |
|---|
| 1026 | | - i2s->field_rxchansel = |
|---|
| 1027 | | - devm_regmap_field_alloc(dev, i2s->regmap, |
|---|
| 1028 | | - i2s->variant->field_rxchansel); |
|---|
| 1029 | | - return PTR_ERR_OR_ZERO(i2s->field_rxchansel); |
|---|
| 1188 | + return 0; |
|---|
| 1030 | 1189 | } |
|---|
| 1031 | 1190 | |
|---|
| 1032 | 1191 | static int sun4i_i2s_probe(struct platform_device *pdev) |
|---|
| .. | .. |
|---|
| 1047 | 1206 | return PTR_ERR(regs); |
|---|
| 1048 | 1207 | |
|---|
| 1049 | 1208 | irq = platform_get_irq(pdev, 0); |
|---|
| 1050 | | - if (irq < 0) { |
|---|
| 1051 | | - dev_err(&pdev->dev, "Can't retrieve our interrupt\n"); |
|---|
| 1209 | + if (irq < 0) |
|---|
| 1052 | 1210 | return irq; |
|---|
| 1053 | | - } |
|---|
| 1054 | 1211 | |
|---|
| 1055 | 1212 | i2s->variant = of_device_get_match_data(&pdev->dev); |
|---|
| 1056 | 1213 | if (!i2s->variant) { |
|---|
| .. | .. |
|---|
| 1108 | 1265 | goto err_pm_disable; |
|---|
| 1109 | 1266 | } |
|---|
| 1110 | 1267 | |
|---|
| 1111 | | - ret = devm_snd_soc_register_component(&pdev->dev, |
|---|
| 1112 | | - &sun4i_i2s_component, |
|---|
| 1113 | | - &sun4i_i2s_dai, 1); |
|---|
| 1268 | + ret = sun4i_i2s_init_regmap_fields(&pdev->dev, i2s); |
|---|
| 1114 | 1269 | if (ret) { |
|---|
| 1115 | | - dev_err(&pdev->dev, "Could not register DAI\n"); |
|---|
| 1270 | + dev_err(&pdev->dev, "Could not initialise regmap fields\n"); |
|---|
| 1116 | 1271 | goto err_suspend; |
|---|
| 1117 | 1272 | } |
|---|
| 1118 | 1273 | |
|---|
| 1119 | | - ret = snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); |
|---|
| 1274 | + ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0); |
|---|
| 1120 | 1275 | if (ret) { |
|---|
| 1121 | 1276 | dev_err(&pdev->dev, "Could not register PCM\n"); |
|---|
| 1122 | 1277 | goto err_suspend; |
|---|
| 1123 | 1278 | } |
|---|
| 1124 | 1279 | |
|---|
| 1125 | | - ret = sun4i_i2s_init_regmap_fields(&pdev->dev, i2s); |
|---|
| 1280 | + ret = devm_snd_soc_register_component(&pdev->dev, |
|---|
| 1281 | + &sun4i_i2s_component, |
|---|
| 1282 | + &sun4i_i2s_dai, 1); |
|---|
| 1126 | 1283 | if (ret) { |
|---|
| 1127 | | - dev_err(&pdev->dev, "Could not initialise regmap fields\n"); |
|---|
| 1284 | + dev_err(&pdev->dev, "Could not register DAI\n"); |
|---|
| 1128 | 1285 | goto err_suspend; |
|---|
| 1129 | 1286 | } |
|---|
| 1130 | 1287 | |
|---|
| .. | .. |
|---|
| 1144 | 1301 | static int sun4i_i2s_remove(struct platform_device *pdev) |
|---|
| 1145 | 1302 | { |
|---|
| 1146 | 1303 | struct sun4i_i2s *i2s = dev_get_drvdata(&pdev->dev); |
|---|
| 1147 | | - |
|---|
| 1148 | | - snd_dmaengine_pcm_unregister(&pdev->dev); |
|---|
| 1149 | 1304 | |
|---|
| 1150 | 1305 | pm_runtime_disable(&pdev->dev); |
|---|
| 1151 | 1306 | if (!pm_runtime_status_suspended(&pdev->dev)) |
|---|
| .. | .. |
|---|
| 1174 | 1329 | .compatible = "allwinner,sun8i-h3-i2s", |
|---|
| 1175 | 1330 | .data = &sun8i_h3_i2s_quirks, |
|---|
| 1176 | 1331 | }, |
|---|
| 1332 | + { |
|---|
| 1333 | + .compatible = "allwinner,sun50i-a64-codec-i2s", |
|---|
| 1334 | + .data = &sun50i_a64_codec_i2s_quirks, |
|---|
| 1335 | + }, |
|---|
| 1177 | 1336 | {} |
|---|
| 1178 | 1337 | }; |
|---|
| 1179 | 1338 | MODULE_DEVICE_TABLE(of, sun4i_i2s_match); |
|---|