Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add support for Loongson-1 AC97

Merge series from Keguang Zhang <keguang.zhang@gmail.com>:

Add the driver and dt-binding document for Loongson-1 AC97.
Add the dt-binding document for Realtek ALC203 Codec.
Add DT support for the AC97 generic codec driver.

+525
+68
Documentation/devicetree/bindings/sound/loongson,ls1b-ac97.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/sound/loongson,ls1b-ac97.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Loongson-1 AC97 Controller 8 + 9 + maintainers: 10 + - Keguang Zhang <keguang.zhang@gmail.com> 11 + 12 + description: 13 + The Loongson-1 AC97 controller supports 2-channel stereo output and input. 14 + It is paired with the DMA engine to handle playback and capture functions. 15 + 16 + allOf: 17 + - $ref: dai-common.yaml# 18 + 19 + properties: 20 + compatible: 21 + oneOf: 22 + - const: loongson,ls1b-ac97 23 + - items: 24 + - enum: 25 + - loongson,ls1a-ac97 26 + - loongson,ls1c-ac97 27 + - const: loongson,ls1b-ac97 28 + 29 + reg: 30 + maxItems: 3 31 + 32 + reg-names: 33 + items: 34 + - const: ac97 35 + - const: audio-tx 36 + - const: audio-rx 37 + 38 + dmas: 39 + maxItems: 2 40 + 41 + dma-names: 42 + items: 43 + - const: tx 44 + - const: rx 45 + 46 + '#sound-dai-cells': 47 + const: 0 48 + 49 + required: 50 + - compatible 51 + - reg 52 + - reg-names 53 + - dmas 54 + - dma-names 55 + - '#sound-dai-cells' 56 + 57 + unevaluatedProperties: false 58 + 59 + examples: 60 + - | 61 + audio-controller@1fe74000 { 62 + compatible = "loongson,ls1b-ac97"; 63 + reg = <0x1fe74000 0x60>, <0x1fe72420 0x4>, <0x1fe74c4c 0x4>; 64 + reg-names = "ac97", "audio-tx", "audio-rx"; 65 + dmas = <&dma 1>, <&dma 2>; 66 + dma-names = "tx", "rx"; 67 + #sound-dai-cells = <0>; 68 + };
+36
Documentation/devicetree/bindings/sound/realtek,alc203.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/sound/realtek,alc203.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: Realtek ALC203 AC97 Audio Codec 8 + 9 + maintainers: 10 + - Keguang Zhang <keguang.zhang@gmail.com> 11 + 12 + description: 13 + ALC203 is a full duplex AC97 2.3 compatible stereo audio codec. 14 + 15 + allOf: 16 + - $ref: dai-common.yaml# 17 + 18 + properties: 19 + compatible: 20 + const: realtek,alc203 21 + 22 + '#sound-dai-cells': 23 + const: 0 24 + 25 + required: 26 + - compatible 27 + - '#sound-dai-cells' 28 + 29 + unevaluatedProperties: false 30 + 31 + examples: 32 + - | 33 + audio-codec { 34 + compatible = "realtek,alc203"; 35 + #sound-dai-cells = <0>; 36 + };
+1
MAINTAINERS
··· 16223 16223 F: arch/mips/loongson32/ 16224 16224 F: drivers/*/*loongson1* 16225 16225 F: drivers/net/ethernet/stmicro/stmmac/dwmac-loongson1.c 16226 + F: sound/soc/loongson/loongson1_ac97.c 16226 16227 16227 16228 MIPS/LOONGSON2EF ARCHITECTURE 16228 16229 M: Jiaxun Yang <jiaxun.yang@flygoat.com>
+10
sound/soc/codecs/ac97.c
··· 13 13 #include <linux/kernel.h> 14 14 #include <linux/device.h> 15 15 #include <linux/module.h> 16 + #include <linux/of.h> 16 17 #include <sound/core.h> 17 18 #include <sound/pcm.h> 18 19 #include <sound/ac97_codec.h> ··· 128 127 &soc_component_dev_ac97, &ac97_dai, 1); 129 128 } 130 129 130 + #ifdef CONFIG_OF 131 + static const struct of_device_id ac97_codec_of_match[] = { 132 + { .compatible = "realtek,alc203", }, 133 + { } 134 + }; 135 + MODULE_DEVICE_TABLE(of, ac97_codec_of_match); 136 + #endif 137 + 131 138 static struct platform_driver ac97_codec_driver = { 132 139 .driver = { 133 140 .name = "ac97-codec", 141 + .of_match_table = of_match_ptr(ac97_codec_of_match), 134 142 }, 135 143 136 144 .probe = ac97_probe,
+10
sound/soc/loongson/Kconfig
··· 37 37 The controller work as a platform device, we can found it in 38 38 Loongson-2K1000 SoCs. 39 39 endmenu 40 + 41 + config SND_LOONGSON1_AC97 42 + tristate "Loongson1 AC97 Support" 43 + depends on LOONGSON1_APB_DMA 44 + select SND_SOC_AC97_CODEC 45 + select SND_SOC_GENERIC_DMAENGINE_PCM 46 + select REGMAP_MMIO 47 + help 48 + Say Y or M if you want to add support for codecs attached to 49 + the Loongson1 AC97 controller.
+2
sound/soc/loongson/Makefile
··· 8 8 9 9 snd-soc-loongson-i2s-y := loongson_i2s.o 10 10 11 + obj-$(CONFIG_SND_LOONGSON1_AC97) += loongson1_ac97.o 12 + 11 13 #Machine Support 12 14 snd-soc-loongson-card-y := loongson_card.o 13 15 obj-$(CONFIG_SND_SOC_LOONGSON_CARD) += snd-soc-loongson-card.o
+398
sound/soc/loongson/loongson1_ac97.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * AC97 Controller Driver for Loongson-1 SoC 4 + * 5 + * Copyright (C) 2025 Keguang Zhang <keguang.zhang@gmail.com> 6 + */ 7 + 8 + #include <linux/bitfield.h> 9 + #include <linux/dma-mapping.h> 10 + #include <linux/init.h> 11 + #include <linux/module.h> 12 + #include <linux/platform_device.h> 13 + #include <linux/regmap.h> 14 + 15 + #include <sound/dmaengine_pcm.h> 16 + #include <sound/pcm.h> 17 + #include <sound/pcm_params.h> 18 + #include <sound/soc.h> 19 + 20 + /* Loongson-1 AC97 Controller Registers */ 21 + #define AC97_CSR 0x0 22 + #define AC97_OCC0 0x4 23 + #define AC97_ICC 0x10 24 + #define AC97_CRAC 0x18 25 + #define AC97_INTRAW 0x54 26 + #define AC97_INTM 0x58 27 + #define AC97_INT_CW_CLR 0x68 28 + #define AC97_INT_CR_CLR 0x6c 29 + 30 + /* Control Status Register Bits (CSR) */ 31 + #define CSR_RESUME BIT(1) 32 + #define CSR_RST_FORCE BIT(0) 33 + 34 + /* MIC Channel Configuration Bits */ 35 + #define M_DMA_EN BIT(22) 36 + #define M_FIFO_THRES GENMASK(21, 20) 37 + #define M_FIFO_THRES_FULL FIELD_PREP(M_FIFO_THRES, 3) 38 + #define M_FIFO_THRES_HALF FIELD_PREP(M_FIFO_THRES, 1) 39 + #define M_FIFO_THRES_QUARTER FIELD_PREP(M_FIFO_THRES, 0) 40 + #define M_SW GENMASK(19, 18) 41 + #define M_SW_16_BITS FIELD_PREP(M_SW, 2) 42 + #define M_SW_8_BITS FIELD_PREP(M_SW, 0) 43 + #define M_VSR BIT(17) 44 + #define M_CH_EN BIT(16) 45 + /* Right Channel Configuration Bits */ 46 + #define R_DMA_EN BIT(14) 47 + #define R_FIFO_THRES GENMASK(13, 12) 48 + #define R_FIFO_THRES_EMPTY FIELD_PREP(R_FIFO_THRES, 3) 49 + #define R_FIFO_THRES_HALF FIELD_PREP(R_FIFO_THRES, 1) 50 + #define R_FIFO_THRES_QUARTER FIELD_PREP(R_FIFO_THRES, 0) 51 + #define R_SW GENMASK(11, 10) 52 + #define R_SW_16_BITS FIELD_PREP(R_SW, 2) 53 + #define R_SW_8_BITS FIELD_PREP(R_SW, 0) 54 + #define R_VSR BIT(9) 55 + #define R_CH_EN BIT(8) 56 + /* Left Channel Configuration Bits */ 57 + #define L_DMA_EN BIT(6) 58 + #define L_FIFO_THRES GENMASK(5, 4) 59 + #define L_FIFO_THRES_EMPTY FIELD_PREP(L_FIFO_THRES, 3) 60 + #define L_FIFO_THRES_HALF FIELD_PREP(L_FIFO_THRES, 1) 61 + #define L_FIFO_THRES_QUARTER FIELD_PREP(L_FIFO_THRES, 0) 62 + #define L_SW GENMASK(3, 2) 63 + #define L_SW_16_BITS FIELD_PREP(L_SW, 2) 64 + #define L_SW_8_BITS FIELD_PREP(L_SW, 0) 65 + #define L_VSR BIT(1) 66 + #define L_CH_EN BIT(0) 67 + 68 + /* Codec Register Access Command Bits (CRAC) */ 69 + #define CODEC_WR BIT(31) 70 + #define CODEC_ADR GENMASK(22, 16) 71 + #define CODEC_DAT GENMASK(15, 0) 72 + 73 + /* Interrupt Register (INTRAW) */ 74 + #define CW_DONE BIT(1) 75 + #define CR_DONE BIT(0) 76 + 77 + #define LS1X_AC97_DMA_TX_EN BIT(31) 78 + #define LS1X_AC97_DMA_STEREO BIT(30) 79 + #define LS1X_AC97_DMA_TX_BYTES GENMASK(29, 28) 80 + #define LS1X_AC97_DMA_TX_4_BYTES FIELD_PREP(LS1X_AC97_DMA_TX_BYTES, 2) 81 + #define LS1X_AC97_DMA_TX_2_BYTES FIELD_PREP(LS1X_AC97_DMA_TX_BYTES, 1) 82 + #define LS1X_AC97_DMA_TX_1_BYTE FIELD_PREP(LS1X_AC97_DMA_TX_BYTES, 0) 83 + #define LS1X_AC97_DMA_DADDR_MASK GENMASK(27, 0) 84 + 85 + #define LS1X_AC97_DMA_FIFO_SIZE 128 86 + 87 + #define LS1X_AC97_TIMEOUT 3000 88 + 89 + struct ls1x_ac97 { 90 + void __iomem *reg_base; 91 + struct regmap *regmap; 92 + dma_addr_t tx_dma_base; 93 + dma_addr_t rx_dma_base; 94 + struct snd_dmaengine_dai_dma_data capture_dma_data; 95 + struct snd_dmaengine_dai_dma_data playback_dma_data; 96 + }; 97 + 98 + static struct ls1x_ac97 *ls1x_ac97; 99 + 100 + static const struct regmap_config ls1x_ac97_regmap_config = { 101 + .reg_bits = 32, 102 + .val_bits = 32, 103 + .reg_stride = 4, 104 + }; 105 + 106 + static void ls1x_ac97_reset(struct snd_ac97 *ac97) 107 + { 108 + int val; 109 + 110 + regmap_write(ls1x_ac97->regmap, AC97_CSR, CSR_RST_FORCE); 111 + regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_CSR, val, 112 + !(val & CSR_RESUME), 0, LS1X_AC97_TIMEOUT); 113 + } 114 + 115 + static void ls1x_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val) 116 + { 117 + int tmp, ret; 118 + 119 + tmp = FIELD_PREP(CODEC_ADR, reg) | FIELD_PREP(CODEC_DAT, val); 120 + regmap_write(ls1x_ac97->regmap, AC97_CRAC, tmp); 121 + ret = regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_INTRAW, tmp, 122 + (tmp & CW_DONE), 0, LS1X_AC97_TIMEOUT); 123 + if (ret) 124 + pr_err("timeout on AC97 write! %d\n", ret); 125 + 126 + regmap_read(ls1x_ac97->regmap, AC97_INT_CW_CLR, &ret); 127 + } 128 + 129 + static unsigned short ls1x_ac97_read(struct snd_ac97 *ac97, unsigned short reg) 130 + { 131 + int val, ret; 132 + 133 + val = CODEC_WR | FIELD_PREP(CODEC_ADR, reg); 134 + regmap_write(ls1x_ac97->regmap, AC97_CRAC, val); 135 + ret = regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_INTRAW, val, 136 + (val & CR_DONE), 0, LS1X_AC97_TIMEOUT); 137 + if (ret) { 138 + pr_err("timeout on AC97 read! %d\n", ret); 139 + return ret; 140 + } 141 + 142 + regmap_read(ls1x_ac97->regmap, AC97_INT_CR_CLR, &ret); 143 + regmap_read(ls1x_ac97->regmap, AC97_CRAC, &ret); 144 + 145 + return (ret & CODEC_DAT); 146 + } 147 + 148 + static void ls1x_ac97_init(struct snd_ac97 *ac97) 149 + { 150 + writel(0, ls1x_ac97->reg_base + AC97_INTRAW); 151 + writel(0, ls1x_ac97->reg_base + AC97_INTM); 152 + 153 + /* Config output channels */ 154 + regmap_update_bits(ls1x_ac97->regmap, AC97_OCC0, 155 + R_DMA_EN | R_FIFO_THRES | R_CH_EN | 156 + L_DMA_EN | L_FIFO_THRES | L_CH_EN, 157 + R_DMA_EN | R_FIFO_THRES_EMPTY | R_CH_EN | 158 + L_DMA_EN | L_FIFO_THRES_EMPTY | L_CH_EN); 159 + 160 + /* Config inputs channel */ 161 + regmap_update_bits(ls1x_ac97->regmap, AC97_ICC, 162 + M_DMA_EN | M_FIFO_THRES | M_CH_EN | 163 + R_DMA_EN | R_FIFO_THRES | R_CH_EN | 164 + L_DMA_EN | L_FIFO_THRES | L_CH_EN, 165 + M_DMA_EN | M_FIFO_THRES_FULL | M_CH_EN | 166 + R_DMA_EN | R_FIFO_THRES_EMPTY | R_CH_EN | 167 + L_DMA_EN | L_FIFO_THRES_EMPTY | L_CH_EN); 168 + 169 + if (ac97->ext_id & AC97_EI_VRA) { 170 + regmap_update_bits(ls1x_ac97->regmap, AC97_OCC0, R_VSR | L_VSR, R_VSR | L_VSR); 171 + regmap_update_bits(ls1x_ac97->regmap, AC97_ICC, M_VSR, M_VSR); 172 + } 173 + } 174 + 175 + static struct snd_ac97_bus_ops ls1x_ac97_ops = { 176 + .reset = ls1x_ac97_reset, 177 + .write = ls1x_ac97_write, 178 + .read = ls1x_ac97_read, 179 + .init = ls1x_ac97_init, 180 + }; 181 + 182 + static int ls1x_ac97_hw_params(struct snd_pcm_substream *substream, 183 + struct snd_pcm_hw_params *params, 184 + struct snd_soc_dai *cpu_dai) 185 + { 186 + struct ls1x_ac97 *ac97 = dev_get_drvdata(cpu_dai->dev); 187 + struct snd_dmaengine_dai_dma_data *dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream); 188 + 189 + switch (params_channels(params)) { 190 + case 1: 191 + dma_data->addr &= ~LS1X_AC97_DMA_STEREO; 192 + break; 193 + case 2: 194 + dma_data->addr |= LS1X_AC97_DMA_STEREO; 195 + break; 196 + default: 197 + dev_err(cpu_dai->dev, "unsupported channels! %d\n", params_channels(params)); 198 + return -EINVAL; 199 + } 200 + 201 + switch (params_format(params)) { 202 + case SNDRV_PCM_FORMAT_S8: 203 + case SNDRV_PCM_FORMAT_U8: 204 + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 205 + regmap_update_bits(ac97->regmap, AC97_OCC0, 206 + R_SW | L_SW, 207 + R_SW_8_BITS | L_SW_8_BITS); 208 + else 209 + regmap_update_bits(ac97->regmap, AC97_ICC, 210 + M_SW | R_SW | L_SW, 211 + M_SW_8_BITS | R_SW_8_BITS | L_SW_8_BITS); 212 + break; 213 + case SNDRV_PCM_FORMAT_S16_LE: 214 + case SNDRV_PCM_FORMAT_U16_LE: 215 + case SNDRV_PCM_FORMAT_S16_BE: 216 + case SNDRV_PCM_FORMAT_U16_BE: 217 + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) 218 + regmap_update_bits(ac97->regmap, AC97_OCC0, 219 + R_SW | L_SW, 220 + R_SW_16_BITS | L_SW_16_BITS); 221 + else 222 + regmap_update_bits(ac97->regmap, AC97_ICC, 223 + M_SW | R_SW | L_SW, 224 + M_SW_16_BITS | R_SW_16_BITS | L_SW_16_BITS); 225 + break; 226 + default: 227 + dev_err(cpu_dai->dev, "unsupported format! %d\n", params_format(params)); 228 + return -EINVAL; 229 + } 230 + 231 + return 0; 232 + } 233 + 234 + static int ls1x_ac97_dai_probe(struct snd_soc_dai *cpu_dai) 235 + { 236 + struct ls1x_ac97 *ac97 = dev_get_drvdata(cpu_dai->dev); 237 + 238 + ac97->capture_dma_data.addr = ac97->rx_dma_base & LS1X_AC97_DMA_DADDR_MASK; 239 + ac97->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 240 + ac97->capture_dma_data.fifo_size = LS1X_AC97_DMA_FIFO_SIZE; 241 + 242 + ac97->playback_dma_data.addr = ac97->tx_dma_base & LS1X_AC97_DMA_DADDR_MASK; 243 + ac97->playback_dma_data.addr |= LS1X_AC97_DMA_TX_4_BYTES; 244 + ac97->playback_dma_data.addr |= LS1X_AC97_DMA_TX_EN; 245 + ac97->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES; 246 + ac97->playback_dma_data.fifo_size = LS1X_AC97_DMA_FIFO_SIZE; 247 + 248 + snd_soc_dai_init_dma_data(cpu_dai, &ac97->playback_dma_data, &ac97->capture_dma_data); 249 + snd_soc_dai_set_drvdata(cpu_dai, ac97); 250 + 251 + return 0; 252 + } 253 + 254 + static const struct snd_soc_dai_ops ls1x_ac97_dai_ops = { 255 + .probe = ls1x_ac97_dai_probe, 256 + .hw_params = ls1x_ac97_hw_params, 257 + }; 258 + 259 + #define LS1X_AC97_FMTS (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 |\ 260 + SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE |\ 261 + SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE) 262 + 263 + static struct snd_soc_dai_driver ls1x_ac97_dai[] = { 264 + { 265 + .name = "ls1x-ac97", 266 + .playback = { 267 + .stream_name = "AC97 Playback", 268 + .channels_min = 1, 269 + .channels_max = 2, 270 + .rates = SNDRV_PCM_RATE_8000_48000, 271 + .formats = LS1X_AC97_FMTS, 272 + }, 273 + .capture = { 274 + .stream_name = "AC97 Capture", 275 + .channels_min = 1, 276 + .channels_max = 2, 277 + .rates = SNDRV_PCM_RATE_8000_48000, 278 + .formats = LS1X_AC97_FMTS, 279 + }, 280 + .ops = &ls1x_ac97_dai_ops, 281 + }, 282 + }; 283 + 284 + static const struct snd_soc_component_driver ls1x_ac97_component = { 285 + .name = KBUILD_MODNAME, 286 + .legacy_dai_naming = 1, 287 + }; 288 + 289 + static int ls1x_ac97_probe(struct platform_device *pdev) 290 + { 291 + struct device *dev = &pdev->dev; 292 + struct ls1x_ac97 *ac97; 293 + struct resource *res; 294 + int ret; 295 + 296 + ac97 = devm_kzalloc(dev, sizeof(struct ls1x_ac97), GFP_KERNEL); 297 + if (!ac97) 298 + return -ENOMEM; 299 + ls1x_ac97 = ac97; 300 + platform_set_drvdata(pdev, ac97); 301 + 302 + ac97->reg_base = devm_platform_ioremap_resource(pdev, 0); 303 + if (IS_ERR(ac97->reg_base)) 304 + return PTR_ERR(ac97->reg_base); 305 + 306 + ac97->regmap = devm_regmap_init_mmio(dev, ac97->reg_base, &ls1x_ac97_regmap_config); 307 + if (IS_ERR(ac97->regmap)) 308 + return dev_err_probe(dev, PTR_ERR(ac97->regmap), "devm_regmap_init_mmio failed\n"); 309 + 310 + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audio-tx"); 311 + if (!res) 312 + return dev_err_probe(dev, -EINVAL, "Missing 'audio-tx' in reg-names property\n"); 313 + 314 + ac97->tx_dma_base = dma_map_resource(dev, res->start, resource_size(res), 315 + DMA_TO_DEVICE, 0); 316 + if (dma_mapping_error(dev, ac97->tx_dma_base)) 317 + return -ENXIO; 318 + 319 + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "audio-rx"); 320 + if (!res) 321 + return dev_err_probe(dev, -EINVAL, "Missing 'audio-rx' in reg-names property\n"); 322 + 323 + ac97->rx_dma_base = dma_map_resource(dev, res->start, resource_size(res), 324 + DMA_FROM_DEVICE, 0); 325 + if (dma_mapping_error(dev, ac97->rx_dma_base)) 326 + return -ENXIO; 327 + 328 + ret = devm_snd_dmaengine_pcm_register(dev, NULL, 0); 329 + if (ret) 330 + dev_err_probe(dev, ret, "failed to register PCM\n"); 331 + 332 + ret = devm_snd_soc_register_component(dev, &ls1x_ac97_component, 333 + ls1x_ac97_dai, ARRAY_SIZE(ls1x_ac97_dai)); 334 + if (ret) 335 + dev_err_probe(dev, ret, "failed to register DAI\n"); 336 + 337 + return snd_soc_set_ac97_ops(&ls1x_ac97_ops); 338 + } 339 + 340 + static void ls1x_ac97_remove(struct platform_device *pdev) 341 + { 342 + ls1x_ac97 = NULL; 343 + snd_soc_set_ac97_ops(NULL); 344 + } 345 + 346 + #ifdef CONFIG_PM_SLEEP 347 + static int ls1x_ac97_suspend(struct device *dev) 348 + { 349 + int val; 350 + 351 + regmap_clear_bits(ls1x_ac97->regmap, AC97_OCC0, R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN); 352 + regmap_clear_bits(ls1x_ac97->regmap, AC97_ICC, 353 + M_DMA_EN | M_CH_EN | R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN); 354 + regmap_set_bits(ls1x_ac97->regmap, AC97_CSR, CSR_RESUME); 355 + 356 + return regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_CSR, val, 357 + (val & CSR_RESUME), 0, LS1X_AC97_TIMEOUT); 358 + } 359 + 360 + static int ls1x_ac97_resume(struct device *dev) 361 + { 362 + int val; 363 + 364 + regmap_set_bits(ls1x_ac97->regmap, AC97_OCC0, R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN); 365 + regmap_set_bits(ls1x_ac97->regmap, AC97_ICC, 366 + M_DMA_EN | M_CH_EN | R_DMA_EN | R_CH_EN | L_DMA_EN | L_CH_EN); 367 + regmap_set_bits(ls1x_ac97->regmap, AC97_CSR, CSR_RESUME); 368 + 369 + return regmap_read_poll_timeout(ls1x_ac97->regmap, AC97_CSR, val, 370 + !(val & CSR_RESUME), 0, LS1X_AC97_TIMEOUT); 371 + } 372 + #endif 373 + 374 + static const struct dev_pm_ops ls1x_ac97_pm_ops = { 375 + SET_SYSTEM_SLEEP_PM_OPS(ls1x_ac97_suspend, ls1x_ac97_resume) 376 + }; 377 + 378 + static const struct of_device_id ls1x_ac97_match[] = { 379 + { .compatible = "loongson,ls1b-ac97" }, 380 + { /* sentinel */ } 381 + }; 382 + MODULE_DEVICE_TABLE(of, ls1x_ac97_match); 383 + 384 + static struct platform_driver ls1x_ac97_driver = { 385 + .probe = ls1x_ac97_probe, 386 + .remove = ls1x_ac97_remove, 387 + .driver = { 388 + .name = KBUILD_MODNAME, 389 + .of_match_table = ls1x_ac97_match, 390 + .pm = &ls1x_ac97_pm_ops, 391 + }, 392 + }; 393 + 394 + module_platform_driver(ls1x_ac97_driver); 395 + 396 + MODULE_AUTHOR("Keguang Zhang <keguang.zhang@gmail.com>"); 397 + MODULE_DESCRIPTION("Loongson-1 AC97 Controller Driver"); 398 + MODULE_LICENSE("GPL");