1 | // SPDX-License-Identifier: GPL-2.0 |
2 | // |
3 | // rt711-sdw.c -- rt711 ALSA SoC audio driver |
4 | // |
5 | // Copyright(c) 2019 Realtek Semiconductor Corp. |
6 | // |
7 | // |
8 | |
9 | #include <linux/delay.h> |
10 | #include <linux/device.h> |
11 | #include <linux/mod_devicetable.h> |
12 | #include <linux/soundwire/sdw.h> |
13 | #include <linux/soundwire/sdw_type.h> |
14 | #include <linux/soundwire/sdw_registers.h> |
15 | #include <linux/module.h> |
16 | #include <linux/pm_runtime.h> |
17 | #include <linux/regmap.h> |
18 | #include <sound/soc.h> |
19 | #include "rt711.h" |
20 | #include "rt711-sdw.h" |
21 | |
22 | static bool rt711_readable_register(struct device *dev, unsigned int reg) |
23 | { |
24 | switch (reg) { |
25 | case 0x00e0: |
26 | case 0x00f0: |
27 | case 0x2012 ... 0x2016: |
28 | case 0x201a ... 0x2027: |
29 | case 0x2029 ... 0x202a: |
30 | case 0x202d ... 0x2034: |
31 | case 0x2201 ... 0x2204: |
32 | case 0x2206 ... 0x2212: |
33 | case 0x2220 ... 0x2223: |
34 | case 0x2230 ... 0x2239: |
35 | case 0x2f01 ... 0x2f0f: |
36 | case 0x3000 ... 0x3fff: |
37 | case 0x7000 ... 0x7fff: |
38 | case 0x8300 ... 0x83ff: |
39 | case 0x9c00 ... 0x9cff: |
40 | case 0xb900 ... 0xb9ff: |
41 | case 0x752009: |
42 | case 0x752011: |
43 | case 0x75201a: |
44 | case 0x752045: |
45 | case 0x752046: |
46 | case 0x752048: |
47 | case 0x75204a: |
48 | case 0x75206b: |
49 | case 0x75206f: |
50 | case 0x752080: |
51 | case 0x752081: |
52 | case 0x752091: |
53 | case 0x755800: |
54 | return true; |
55 | default: |
56 | return false; |
57 | } |
58 | } |
59 | |
60 | static bool rt711_volatile_register(struct device *dev, unsigned int reg) |
61 | { |
62 | switch (reg) { |
63 | case 0x2016: |
64 | case 0x201b: |
65 | case 0x201c: |
66 | case 0x201d: |
67 | case 0x201f: |
68 | case 0x2021: |
69 | case 0x2023: |
70 | case 0x2230: |
71 | case 0x2012 ... 0x2015: /* HD-A read */ |
72 | case 0x202d ... 0x202f: /* BRA */ |
73 | case 0x2201 ... 0x2212: /* i2c debug */ |
74 | case 0x2220 ... 0x2223: /* decoded HD-A */ |
75 | case 0x9c00 ... 0x9cff: |
76 | case 0xb900 ... 0xb9ff: |
77 | case 0xff01: |
78 | case 0x75201a: |
79 | case 0x752046: |
80 | case 0x752080: |
81 | case 0x752081: |
82 | case 0x755800: |
83 | return true; |
84 | default: |
85 | return false; |
86 | } |
87 | } |
88 | |
89 | static int rt711_sdw_read(void *context, unsigned int reg, unsigned int *val) |
90 | { |
91 | struct device *dev = context; |
92 | struct rt711_priv *rt711 = dev_get_drvdata(dev); |
93 | unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; |
94 | unsigned int reg2 = 0, reg3 = 0, reg4 = 0, mask, nid, val2; |
95 | unsigned int is_hda_reg = 1, is_index_reg = 0; |
96 | int ret; |
97 | |
98 | if (reg > 0xffff) |
99 | is_index_reg = 1; |
100 | |
101 | mask = reg & 0xf000; |
102 | |
103 | if (is_index_reg) { /* index registers */ |
104 | val2 = reg & 0xff; |
105 | reg = reg >> 8; |
106 | nid = reg & 0xff; |
107 | ret = regmap_write(map: rt711->sdw_regmap, reg, val: 0); |
108 | if (ret < 0) |
109 | return ret; |
110 | reg2 = reg + 0x1000; |
111 | reg2 |= 0x80; |
112 | ret = regmap_write(map: rt711->sdw_regmap, reg: reg2, val: val2); |
113 | if (ret < 0) |
114 | return ret; |
115 | |
116 | reg3 = RT711_PRIV_DATA_R_H | nid; |
117 | ret = regmap_write(map: rt711->sdw_regmap, |
118 | reg: reg3, val: ((*val >> 8) & 0xff)); |
119 | if (ret < 0) |
120 | return ret; |
121 | reg4 = reg3 + 0x1000; |
122 | reg4 |= 0x80; |
123 | ret = regmap_write(map: rt711->sdw_regmap, reg: reg4, val: (*val & 0xff)); |
124 | if (ret < 0) |
125 | return ret; |
126 | } else if (mask == 0x3000) { |
127 | reg += 0x8000; |
128 | ret = regmap_write(map: rt711->sdw_regmap, reg, val: *val); |
129 | if (ret < 0) |
130 | return ret; |
131 | } else if (mask == 0x7000) { |
132 | reg += 0x2000; |
133 | reg |= 0x800; |
134 | ret = regmap_write(map: rt711->sdw_regmap, |
135 | reg, val: ((*val >> 8) & 0xff)); |
136 | if (ret < 0) |
137 | return ret; |
138 | reg2 = reg + 0x1000; |
139 | reg2 |= 0x80; |
140 | ret = regmap_write(map: rt711->sdw_regmap, reg: reg2, val: (*val & 0xff)); |
141 | if (ret < 0) |
142 | return ret; |
143 | } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ |
144 | reg2 = reg - 0x1000; |
145 | reg2 &= ~0x80; |
146 | ret = regmap_write(map: rt711->sdw_regmap, |
147 | reg: reg2, val: ((*val >> 8) & 0xff)); |
148 | if (ret < 0) |
149 | return ret; |
150 | ret = regmap_write(map: rt711->sdw_regmap, reg, val: (*val & 0xff)); |
151 | if (ret < 0) |
152 | return ret; |
153 | } else if (mask == 0x9000) { |
154 | ret = regmap_write(map: rt711->sdw_regmap, |
155 | reg, val: ((*val >> 8) & 0xff)); |
156 | if (ret < 0) |
157 | return ret; |
158 | reg2 = reg + 0x1000; |
159 | reg2 |= 0x80; |
160 | ret = regmap_write(map: rt711->sdw_regmap, reg: reg2, val: (*val & 0xff)); |
161 | if (ret < 0) |
162 | return ret; |
163 | } else if (mask == 0xb000) { |
164 | ret = regmap_write(map: rt711->sdw_regmap, reg, val: *val); |
165 | if (ret < 0) |
166 | return ret; |
167 | } else { |
168 | ret = regmap_read(map: rt711->sdw_regmap, reg, val); |
169 | if (ret < 0) |
170 | return ret; |
171 | is_hda_reg = 0; |
172 | } |
173 | |
174 | if (is_hda_reg || is_index_reg) { |
175 | sdw_data_3 = 0; |
176 | sdw_data_2 = 0; |
177 | sdw_data_1 = 0; |
178 | sdw_data_0 = 0; |
179 | ret = regmap_read(map: rt711->sdw_regmap, |
180 | RT711_READ_HDA_3, val: &sdw_data_3); |
181 | if (ret < 0) |
182 | return ret; |
183 | ret = regmap_read(map: rt711->sdw_regmap, |
184 | RT711_READ_HDA_2, val: &sdw_data_2); |
185 | if (ret < 0) |
186 | return ret; |
187 | ret = regmap_read(map: rt711->sdw_regmap, |
188 | RT711_READ_HDA_1, val: &sdw_data_1); |
189 | if (ret < 0) |
190 | return ret; |
191 | ret = regmap_read(map: rt711->sdw_regmap, |
192 | RT711_READ_HDA_0, val: &sdw_data_0); |
193 | if (ret < 0) |
194 | return ret; |
195 | *val = ((sdw_data_3 & 0xff) << 24) | |
196 | ((sdw_data_2 & 0xff) << 16) | |
197 | ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); |
198 | } |
199 | |
200 | if (is_hda_reg == 0) |
201 | dev_dbg(dev, "[%s] %04x => %08x\n" , __func__, reg, *val); |
202 | else if (is_index_reg) |
203 | dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n" , |
204 | __func__, reg, reg2, reg3, reg4, *val); |
205 | else |
206 | dev_dbg(dev, "[%s] %04x %04x => %08x\n" , |
207 | __func__, reg, reg2, *val); |
208 | |
209 | return 0; |
210 | } |
211 | |
212 | static int rt711_sdw_write(void *context, unsigned int reg, unsigned int val) |
213 | { |
214 | struct device *dev = context; |
215 | struct rt711_priv *rt711 = dev_get_drvdata(dev); |
216 | unsigned int reg2 = 0, reg3, reg4, nid, mask, val2; |
217 | unsigned int is_index_reg = 0; |
218 | int ret; |
219 | |
220 | if (reg > 0xffff) |
221 | is_index_reg = 1; |
222 | |
223 | mask = reg & 0xf000; |
224 | |
225 | if (is_index_reg) { /* index registers */ |
226 | val2 = reg & 0xff; |
227 | reg = reg >> 8; |
228 | nid = reg & 0xff; |
229 | ret = regmap_write(map: rt711->sdw_regmap, reg, val: 0); |
230 | if (ret < 0) |
231 | return ret; |
232 | reg2 = reg + 0x1000; |
233 | reg2 |= 0x80; |
234 | ret = regmap_write(map: rt711->sdw_regmap, reg: reg2, val: val2); |
235 | if (ret < 0) |
236 | return ret; |
237 | |
238 | reg3 = RT711_PRIV_DATA_W_H | nid; |
239 | ret = regmap_write(map: rt711->sdw_regmap, |
240 | reg: reg3, val: ((val >> 8) & 0xff)); |
241 | if (ret < 0) |
242 | return ret; |
243 | reg4 = reg3 + 0x1000; |
244 | reg4 |= 0x80; |
245 | ret = regmap_write(map: rt711->sdw_regmap, reg: reg4, val: (val & 0xff)); |
246 | if (ret < 0) |
247 | return ret; |
248 | is_index_reg = 1; |
249 | } else if (reg < 0x4fff) { |
250 | ret = regmap_write(map: rt711->sdw_regmap, reg, val); |
251 | if (ret < 0) |
252 | return ret; |
253 | } else if (reg == RT711_FUNC_RESET) { |
254 | ret = regmap_write(map: rt711->sdw_regmap, reg, val); |
255 | if (ret < 0) |
256 | return ret; |
257 | } else if (mask == 0x7000) { |
258 | ret = regmap_write(map: rt711->sdw_regmap, |
259 | reg, val: ((val >> 8) & 0xff)); |
260 | if (ret < 0) |
261 | return ret; |
262 | reg2 = reg + 0x1000; |
263 | reg2 |= 0x80; |
264 | ret = regmap_write(map: rt711->sdw_regmap, reg: reg2, val: (val & 0xff)); |
265 | if (ret < 0) |
266 | return ret; |
267 | } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ |
268 | reg2 = reg - 0x1000; |
269 | reg2 &= ~0x80; |
270 | ret = regmap_write(map: rt711->sdw_regmap, |
271 | reg: reg2, val: ((val >> 8) & 0xff)); |
272 | if (ret < 0) |
273 | return ret; |
274 | ret = regmap_write(map: rt711->sdw_regmap, reg, val: (val & 0xff)); |
275 | if (ret < 0) |
276 | return ret; |
277 | } |
278 | |
279 | if (reg2 == 0) |
280 | dev_dbg(dev, "[%s] %04x <= %04x\n" , __func__, reg, val); |
281 | else if (is_index_reg) |
282 | dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n" , |
283 | __func__, reg, reg2, reg3, reg4, val2, val); |
284 | else |
285 | dev_dbg(dev, "[%s] %04x %04x <= %04x\n" , |
286 | __func__, reg, reg2, val); |
287 | |
288 | return 0; |
289 | } |
290 | |
291 | static const struct regmap_config rt711_regmap = { |
292 | .reg_bits = 24, |
293 | .val_bits = 32, |
294 | .readable_reg = rt711_readable_register, |
295 | .volatile_reg = rt711_volatile_register, |
296 | .max_register = 0x755800, |
297 | .reg_defaults = rt711_reg_defaults, |
298 | .num_reg_defaults = ARRAY_SIZE(rt711_reg_defaults), |
299 | .cache_type = REGCACHE_MAPLE, |
300 | .use_single_read = true, |
301 | .use_single_write = true, |
302 | .reg_read = rt711_sdw_read, |
303 | .reg_write = rt711_sdw_write, |
304 | }; |
305 | |
306 | static const struct regmap_config rt711_sdw_regmap = { |
307 | .name = "sdw" , |
308 | .reg_bits = 32, |
309 | .val_bits = 8, |
310 | .readable_reg = rt711_readable_register, |
311 | .max_register = 0xff01, |
312 | .cache_type = REGCACHE_NONE, |
313 | .use_single_read = true, |
314 | .use_single_write = true, |
315 | }; |
316 | |
317 | static int rt711_update_status(struct sdw_slave *slave, |
318 | enum sdw_slave_status status) |
319 | { |
320 | struct rt711_priv *rt711 = dev_get_drvdata(dev: &slave->dev); |
321 | |
322 | if (status == SDW_SLAVE_UNATTACHED) |
323 | rt711->hw_init = false; |
324 | |
325 | /* |
326 | * Perform initialization only if slave status is present and |
327 | * hw_init flag is false |
328 | */ |
329 | if (rt711->hw_init || status != SDW_SLAVE_ATTACHED) |
330 | return 0; |
331 | |
332 | /* perform I/O transfers required for Slave initialization */ |
333 | return rt711_io_init(dev: &slave->dev, slave); |
334 | } |
335 | |
336 | static int rt711_read_prop(struct sdw_slave *slave) |
337 | { |
338 | struct sdw_slave_prop *prop = &slave->prop; |
339 | int nval; |
340 | int i, j; |
341 | u32 bit; |
342 | unsigned long addr; |
343 | struct sdw_dpn_prop *dpn; |
344 | |
345 | prop->scp_int1_mask = SDW_SCP_INT1_IMPL_DEF | SDW_SCP_INT1_BUS_CLASH | |
346 | SDW_SCP_INT1_PARITY; |
347 | prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; |
348 | |
349 | prop->paging_support = false; |
350 | |
351 | /* first we need to allocate memory for set bits in port lists */ |
352 | prop->source_ports = 0x14; /* BITMAP: 00010100 */ |
353 | prop->sink_ports = 0x8; /* BITMAP: 00001000 */ |
354 | |
355 | nval = hweight32(prop->source_ports); |
356 | prop->src_dpn_prop = devm_kcalloc(dev: &slave->dev, n: nval, |
357 | size: sizeof(*prop->src_dpn_prop), |
358 | GFP_KERNEL); |
359 | if (!prop->src_dpn_prop) |
360 | return -ENOMEM; |
361 | |
362 | i = 0; |
363 | dpn = prop->src_dpn_prop; |
364 | addr = prop->source_ports; |
365 | for_each_set_bit(bit, &addr, 32) { |
366 | dpn[i].num = bit; |
367 | dpn[i].type = SDW_DPN_FULL; |
368 | dpn[i].simple_ch_prep_sm = true; |
369 | dpn[i].ch_prep_timeout = 10; |
370 | i++; |
371 | } |
372 | |
373 | /* do this again for sink now */ |
374 | nval = hweight32(prop->sink_ports); |
375 | prop->sink_dpn_prop = devm_kcalloc(dev: &slave->dev, n: nval, |
376 | size: sizeof(*prop->sink_dpn_prop), |
377 | GFP_KERNEL); |
378 | if (!prop->sink_dpn_prop) |
379 | return -ENOMEM; |
380 | |
381 | j = 0; |
382 | dpn = prop->sink_dpn_prop; |
383 | addr = prop->sink_ports; |
384 | for_each_set_bit(bit, &addr, 32) { |
385 | dpn[j].num = bit; |
386 | dpn[j].type = SDW_DPN_FULL; |
387 | dpn[j].simple_ch_prep_sm = true; |
388 | dpn[j].ch_prep_timeout = 10; |
389 | j++; |
390 | } |
391 | |
392 | /* set the timeout values */ |
393 | prop->clk_stop_timeout = 20; |
394 | |
395 | /* wake-up event */ |
396 | prop->wake_capable = 1; |
397 | |
398 | return 0; |
399 | } |
400 | |
401 | static int rt711_bus_config(struct sdw_slave *slave, |
402 | struct sdw_bus_params *params) |
403 | { |
404 | struct rt711_priv *rt711 = dev_get_drvdata(dev: &slave->dev); |
405 | int ret; |
406 | |
407 | memcpy(&rt711->params, params, sizeof(*params)); |
408 | |
409 | ret = rt711_clock_config(dev: &slave->dev); |
410 | if (ret < 0) |
411 | dev_err(&slave->dev, "%s: Invalid clk config" , __func__); |
412 | |
413 | return ret; |
414 | } |
415 | |
416 | static int rt711_interrupt_callback(struct sdw_slave *slave, |
417 | struct sdw_slave_intr_status *status) |
418 | { |
419 | struct rt711_priv *rt711 = dev_get_drvdata(dev: &slave->dev); |
420 | |
421 | dev_dbg(&slave->dev, |
422 | "%s control_port_stat=%x" , __func__, status->control_port); |
423 | |
424 | mutex_lock(&rt711->disable_irq_lock); |
425 | if (status->control_port & 0x4 && !rt711->disable_irq) { |
426 | mod_delayed_work(wq: system_power_efficient_wq, |
427 | dwork: &rt711->jack_detect_work, delay: msecs_to_jiffies(m: 250)); |
428 | } |
429 | mutex_unlock(lock: &rt711->disable_irq_lock); |
430 | |
431 | return 0; |
432 | } |
433 | |
434 | static const struct sdw_slave_ops rt711_slave_ops = { |
435 | .read_prop = rt711_read_prop, |
436 | .interrupt_callback = rt711_interrupt_callback, |
437 | .update_status = rt711_update_status, |
438 | .bus_config = rt711_bus_config, |
439 | }; |
440 | |
441 | static int rt711_sdw_probe(struct sdw_slave *slave, |
442 | const struct sdw_device_id *id) |
443 | { |
444 | struct regmap *sdw_regmap, *regmap; |
445 | |
446 | /* Regmap Initialization */ |
447 | sdw_regmap = devm_regmap_init_sdw(slave, &rt711_sdw_regmap); |
448 | if (IS_ERR(ptr: sdw_regmap)) |
449 | return PTR_ERR(ptr: sdw_regmap); |
450 | |
451 | regmap = devm_regmap_init(&slave->dev, NULL, |
452 | &slave->dev, &rt711_regmap); |
453 | if (IS_ERR(ptr: regmap)) |
454 | return PTR_ERR(ptr: regmap); |
455 | |
456 | return rt711_init(dev: &slave->dev, sdw_regmap, regmap, slave); |
457 | } |
458 | |
459 | static int rt711_sdw_remove(struct sdw_slave *slave) |
460 | { |
461 | struct rt711_priv *rt711 = dev_get_drvdata(dev: &slave->dev); |
462 | |
463 | if (rt711->hw_init) { |
464 | cancel_delayed_work_sync(dwork: &rt711->jack_detect_work); |
465 | cancel_delayed_work_sync(dwork: &rt711->jack_btn_check_work); |
466 | cancel_work_sync(work: &rt711->calibration_work); |
467 | } |
468 | |
469 | pm_runtime_disable(dev: &slave->dev); |
470 | |
471 | mutex_destroy(lock: &rt711->calibrate_mutex); |
472 | mutex_destroy(lock: &rt711->disable_irq_lock); |
473 | |
474 | return 0; |
475 | } |
476 | |
477 | static const struct sdw_device_id rt711_id[] = { |
478 | SDW_SLAVE_ENTRY_EXT(0x025d, 0x711, 0x2, 0, 0), |
479 | {}, |
480 | }; |
481 | MODULE_DEVICE_TABLE(sdw, rt711_id); |
482 | |
483 | static int __maybe_unused rt711_dev_suspend(struct device *dev) |
484 | { |
485 | struct rt711_priv *rt711 = dev_get_drvdata(dev); |
486 | |
487 | if (!rt711->hw_init) |
488 | return 0; |
489 | |
490 | cancel_delayed_work_sync(dwork: &rt711->jack_detect_work); |
491 | cancel_delayed_work_sync(dwork: &rt711->jack_btn_check_work); |
492 | cancel_work_sync(work: &rt711->calibration_work); |
493 | |
494 | regcache_cache_only(map: rt711->regmap, enable: true); |
495 | |
496 | return 0; |
497 | } |
498 | |
499 | static int __maybe_unused rt711_dev_system_suspend(struct device *dev) |
500 | { |
501 | struct rt711_priv *rt711 = dev_get_drvdata(dev); |
502 | struct sdw_slave *slave = dev_to_sdw_dev(dev); |
503 | int ret; |
504 | |
505 | if (!rt711->hw_init) |
506 | return 0; |
507 | |
508 | /* |
509 | * prevent new interrupts from being handled after the |
510 | * deferred work completes and before the parent disables |
511 | * interrupts on the link |
512 | */ |
513 | mutex_lock(&rt711->disable_irq_lock); |
514 | rt711->disable_irq = true; |
515 | ret = sdw_update_no_pm(slave, SDW_SCP_INTMASK1, |
516 | SDW_SCP_INT1_IMPL_DEF, val: 0); |
517 | mutex_unlock(lock: &rt711->disable_irq_lock); |
518 | |
519 | if (ret < 0) { |
520 | /* log but don't prevent suspend from happening */ |
521 | dev_dbg(&slave->dev, "%s: could not disable imp-def interrupts\n:" , __func__); |
522 | } |
523 | |
524 | return rt711_dev_suspend(dev); |
525 | } |
526 | |
527 | #define RT711_PROBE_TIMEOUT 5000 |
528 | |
529 | static int __maybe_unused rt711_dev_resume(struct device *dev) |
530 | { |
531 | struct sdw_slave *slave = dev_to_sdw_dev(dev); |
532 | struct rt711_priv *rt711 = dev_get_drvdata(dev); |
533 | unsigned long time; |
534 | |
535 | if (!rt711->first_hw_init) |
536 | return 0; |
537 | |
538 | if (!slave->unattach_request) { |
539 | mutex_lock(&rt711->disable_irq_lock); |
540 | if (rt711->disable_irq == true) { |
541 | sdw_write_no_pm(slave, SDW_SCP_INTMASK1, SDW_SCP_INT1_IMPL_DEF); |
542 | rt711->disable_irq = false; |
543 | } |
544 | mutex_unlock(lock: &rt711->disable_irq_lock); |
545 | goto regmap_sync; |
546 | } |
547 | |
548 | time = wait_for_completion_timeout(x: &slave->initialization_complete, |
549 | timeout: msecs_to_jiffies(RT711_PROBE_TIMEOUT)); |
550 | if (!time) { |
551 | dev_err(&slave->dev, "%s: Initialization not complete, timed out\n" , __func__); |
552 | return -ETIMEDOUT; |
553 | } |
554 | |
555 | regmap_sync: |
556 | slave->unattach_request = 0; |
557 | regcache_cache_only(map: rt711->regmap, enable: false); |
558 | regcache_sync_region(map: rt711->regmap, min: 0x3000, max: 0x8fff); |
559 | regcache_sync_region(map: rt711->regmap, min: 0x752009, max: 0x752091); |
560 | |
561 | return 0; |
562 | } |
563 | |
564 | static const struct dev_pm_ops rt711_pm = { |
565 | SET_SYSTEM_SLEEP_PM_OPS(rt711_dev_system_suspend, rt711_dev_resume) |
566 | SET_RUNTIME_PM_OPS(rt711_dev_suspend, rt711_dev_resume, NULL) |
567 | }; |
568 | |
569 | static struct sdw_driver rt711_sdw_driver = { |
570 | .driver = { |
571 | .name = "rt711" , |
572 | .owner = THIS_MODULE, |
573 | .pm = &rt711_pm, |
574 | }, |
575 | .probe = rt711_sdw_probe, |
576 | .remove = rt711_sdw_remove, |
577 | .ops = &rt711_slave_ops, |
578 | .id_table = rt711_id, |
579 | }; |
580 | module_sdw_driver(rt711_sdw_driver); |
581 | |
582 | MODULE_DESCRIPTION("ASoC RT711 SDW driver" ); |
583 | MODULE_AUTHOR("Shuming Fan <shumingf@realtek.com>" ); |
584 | MODULE_LICENSE("GPL" ); |
585 | |