1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * rt715-sdw.c -- rt715 ALSA SoC audio driver |
4 | * |
5 | * Copyright(c) 2019 Realtek Semiconductor Corp. |
6 | * |
7 | * ALC715 ASoC Codec Driver based Intel Dummy SdW codec driver |
8 | * |
9 | */ |
10 | #include <linux/delay.h> |
11 | #include <linux/device.h> |
12 | #include <linux/mod_devicetable.h> |
13 | #include <linux/soundwire/sdw.h> |
14 | #include <linux/soundwire/sdw_type.h> |
15 | #include <linux/soundwire/sdw_registers.h> |
16 | #include <linux/module.h> |
17 | #include <linux/pm_runtime.h> |
18 | #include <linux/of.h> |
19 | #include <linux/regmap.h> |
20 | #include <sound/soc.h> |
21 | #include "rt715.h" |
22 | #include "rt715-sdw.h" |
23 | |
24 | static bool rt715_readable_register(struct device *dev, unsigned int reg) |
25 | { |
26 | switch (reg) { |
27 | case 0x00e0 ... 0x00e5: |
28 | case 0x00ee ... 0x00ef: |
29 | case 0x00f0 ... 0x00f5: |
30 | case 0x00fe ... 0x00ff: |
31 | case 0x02e0: |
32 | case 0x02f0: |
33 | case 0x04e0: |
34 | case 0x04f0: |
35 | case 0x06e0: |
36 | case 0x06f0: |
37 | case 0x2000 ... 0x2016: |
38 | case 0x201a ... 0x2027: |
39 | case 0x2029 ... 0x202a: |
40 | case 0x202d ... 0x2034: |
41 | case 0x2200 ... 0x2204: |
42 | case 0x2206 ... 0x2212: |
43 | case 0x2220 ... 0x2223: |
44 | case 0x2230 ... 0x2239: |
45 | case 0x22f0 ... 0x22f3: |
46 | case 0x3122: |
47 | case 0x3123: |
48 | case 0x3124: |
49 | case 0x3125: |
50 | case 0x3607: |
51 | case 0x3608: |
52 | case 0x3609: |
53 | case 0x3610: |
54 | case 0x3611: |
55 | case 0x3627: |
56 | case 0x3712: |
57 | case 0x3713: |
58 | case 0x3718: |
59 | case 0x3719: |
60 | case 0x371a: |
61 | case 0x371b: |
62 | case 0x371d: |
63 | case 0x3729: |
64 | case 0x385e: |
65 | case 0x3859: |
66 | case 0x4c12: |
67 | case 0x4c13: |
68 | case 0x4c1d: |
69 | case 0x4c29: |
70 | case 0x4d12: |
71 | case 0x4d13: |
72 | case 0x4d1d: |
73 | case 0x4d29: |
74 | case 0x4e12: |
75 | case 0x4e13: |
76 | case 0x4e1d: |
77 | case 0x4e29: |
78 | case 0x4f12: |
79 | case 0x4f13: |
80 | case 0x4f1d: |
81 | case 0x4f29: |
82 | case 0x7207: |
83 | case 0x7208: |
84 | case 0x7209: |
85 | case 0x7227: |
86 | case 0x7307: |
87 | case 0x7308: |
88 | case 0x7309: |
89 | case 0x7312: |
90 | case 0x7313: |
91 | case 0x7318: |
92 | case 0x7319: |
93 | case 0x731a: |
94 | case 0x731b: |
95 | case 0x731d: |
96 | case 0x7327: |
97 | case 0x7329: |
98 | case 0x8287: |
99 | case 0x8288: |
100 | case 0x8289: |
101 | case 0x82a7: |
102 | case 0x8387: |
103 | case 0x8388: |
104 | case 0x8389: |
105 | case 0x8392: |
106 | case 0x8393: |
107 | case 0x8398: |
108 | case 0x8399: |
109 | case 0x839a: |
110 | case 0x839b: |
111 | case 0x839d: |
112 | case 0x83a7: |
113 | case 0x83a9: |
114 | case 0x752039: |
115 | return true; |
116 | default: |
117 | return false; |
118 | } |
119 | } |
120 | |
121 | static bool rt715_volatile_register(struct device *dev, unsigned int reg) |
122 | { |
123 | switch (reg) { |
124 | case 0x00e5: |
125 | case 0x00f0: |
126 | case 0x00f3: |
127 | case 0x00f5: |
128 | case 0x2009: |
129 | case 0x2016: |
130 | case 0x201b: |
131 | case 0x201c: |
132 | case 0x201d: |
133 | case 0x201f: |
134 | case 0x2023: |
135 | case 0x2230: |
136 | case 0x200b ... 0x200e: /* i2c read */ |
137 | case 0x2012 ... 0x2015: /* HD-A read */ |
138 | case 0x202d ... 0x202f: /* BRA */ |
139 | case 0x2201 ... 0x2212: /* i2c debug */ |
140 | case 0x2220 ... 0x2223: /* decoded HD-A */ |
141 | return true; |
142 | default: |
143 | return false; |
144 | } |
145 | } |
146 | |
147 | static int rt715_sdw_read(void *context, unsigned int reg, unsigned int *val) |
148 | { |
149 | struct device *dev = context; |
150 | struct rt715_priv *rt715 = dev_get_drvdata(dev); |
151 | unsigned int sdw_data_3, sdw_data_2, sdw_data_1, sdw_data_0; |
152 | unsigned int reg2 = 0, reg3 = 0, reg4 = 0, mask, nid, val2; |
153 | unsigned int is_hda_reg = 1, is_index_reg = 0; |
154 | int ret; |
155 | |
156 | if (reg > 0xffff) |
157 | is_index_reg = 1; |
158 | |
159 | mask = reg & 0xf000; |
160 | |
161 | if (is_index_reg) { /* index registers */ |
162 | val2 = reg & 0xff; |
163 | reg = reg >> 8; |
164 | nid = reg & 0xff; |
165 | ret = regmap_write(map: rt715->sdw_regmap, reg, val: 0); |
166 | if (ret < 0) |
167 | return ret; |
168 | reg2 = reg + 0x1000; |
169 | reg2 |= 0x80; |
170 | ret = regmap_write(map: rt715->sdw_regmap, reg: reg2, val: val2); |
171 | if (ret < 0) |
172 | return ret; |
173 | |
174 | reg3 = RT715_PRIV_DATA_R_H | nid; |
175 | ret = regmap_write(map: rt715->sdw_regmap, reg: reg3, |
176 | val: ((*val >> 8) & 0xff)); |
177 | if (ret < 0) |
178 | return ret; |
179 | reg4 = reg3 + 0x1000; |
180 | reg4 |= 0x80; |
181 | ret = regmap_write(map: rt715->sdw_regmap, reg: reg4, val: (*val & 0xff)); |
182 | if (ret < 0) |
183 | return ret; |
184 | } else if (mask == 0x3000) { |
185 | reg += 0x8000; |
186 | ret = regmap_write(map: rt715->sdw_regmap, reg, val: *val); |
187 | if (ret < 0) |
188 | return ret; |
189 | } else if (mask == 0x7000) { |
190 | reg += 0x2000; |
191 | reg |= 0x800; |
192 | ret = regmap_write(map: rt715->sdw_regmap, reg, |
193 | val: ((*val >> 8) & 0xff)); |
194 | if (ret < 0) |
195 | return ret; |
196 | reg2 = reg + 0x1000; |
197 | reg2 |= 0x80; |
198 | ret = regmap_write(map: rt715->sdw_regmap, reg: reg2, val: (*val & 0xff)); |
199 | if (ret < 0) |
200 | return ret; |
201 | } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ |
202 | reg2 = reg - 0x1000; |
203 | reg2 &= ~0x80; |
204 | ret = regmap_write(map: rt715->sdw_regmap, reg: reg2, |
205 | val: ((*val >> 8) & 0xff)); |
206 | if (ret < 0) |
207 | return ret; |
208 | ret = regmap_write(map: rt715->sdw_regmap, reg, val: (*val & 0xff)); |
209 | if (ret < 0) |
210 | return ret; |
211 | } else if (mask == 0x9000) { |
212 | ret = regmap_write(map: rt715->sdw_regmap, reg, |
213 | val: ((*val >> 8) & 0xff)); |
214 | if (ret < 0) |
215 | return ret; |
216 | reg2 = reg + 0x1000; |
217 | reg2 |= 0x80; |
218 | ret = regmap_write(map: rt715->sdw_regmap, reg: reg2, val: (*val & 0xff)); |
219 | if (ret < 0) |
220 | return ret; |
221 | } else if (mask == 0xb000) { |
222 | ret = regmap_write(map: rt715->sdw_regmap, reg, val: *val); |
223 | if (ret < 0) |
224 | return ret; |
225 | } else { |
226 | ret = regmap_read(map: rt715->sdw_regmap, reg, val); |
227 | if (ret < 0) |
228 | return ret; |
229 | is_hda_reg = 0; |
230 | } |
231 | |
232 | if (is_hda_reg || is_index_reg) { |
233 | sdw_data_3 = 0; |
234 | sdw_data_2 = 0; |
235 | sdw_data_1 = 0; |
236 | sdw_data_0 = 0; |
237 | ret = regmap_read(map: rt715->sdw_regmap, RT715_READ_HDA_3, |
238 | val: &sdw_data_3); |
239 | if (ret < 0) |
240 | return ret; |
241 | ret = regmap_read(map: rt715->sdw_regmap, RT715_READ_HDA_2, |
242 | val: &sdw_data_2); |
243 | if (ret < 0) |
244 | return ret; |
245 | ret = regmap_read(map: rt715->sdw_regmap, RT715_READ_HDA_1, |
246 | val: &sdw_data_1); |
247 | if (ret < 0) |
248 | return ret; |
249 | ret = regmap_read(map: rt715->sdw_regmap, RT715_READ_HDA_0, |
250 | val: &sdw_data_0); |
251 | if (ret < 0) |
252 | return ret; |
253 | *val = ((sdw_data_3 & 0xff) << 24) | |
254 | ((sdw_data_2 & 0xff) << 16) | |
255 | ((sdw_data_1 & 0xff) << 8) | (sdw_data_0 & 0xff); |
256 | } |
257 | |
258 | if (is_hda_reg == 0) |
259 | dev_dbg(dev, "[%s] %04x => %08x\n" , __func__, reg, *val); |
260 | else if (is_index_reg) |
261 | dev_dbg(dev, "[%s] %04x %04x %04x %04x => %08x\n" , __func__, |
262 | reg, reg2, reg3, reg4, *val); |
263 | else |
264 | dev_dbg(dev, "[%s] %04x %04x => %08x\n" , |
265 | __func__, reg, reg2, *val); |
266 | |
267 | return 0; |
268 | } |
269 | |
270 | static int rt715_sdw_write(void *context, unsigned int reg, unsigned int val) |
271 | { |
272 | struct device *dev = context; |
273 | struct rt715_priv *rt715 = dev_get_drvdata(dev); |
274 | unsigned int reg2 = 0, reg3, reg4, nid, mask, val2; |
275 | unsigned int is_index_reg = 0; |
276 | int ret; |
277 | |
278 | if (reg > 0xffff) |
279 | is_index_reg = 1; |
280 | |
281 | mask = reg & 0xf000; |
282 | |
283 | if (is_index_reg) { /* index registers */ |
284 | val2 = reg & 0xff; |
285 | reg = reg >> 8; |
286 | nid = reg & 0xff; |
287 | ret = regmap_write(map: rt715->sdw_regmap, reg, val: 0); |
288 | if (ret < 0) |
289 | return ret; |
290 | reg2 = reg + 0x1000; |
291 | reg2 |= 0x80; |
292 | ret = regmap_write(map: rt715->sdw_regmap, reg: reg2, val: val2); |
293 | if (ret < 0) |
294 | return ret; |
295 | |
296 | reg3 = RT715_PRIV_DATA_W_H | nid; |
297 | ret = regmap_write(map: rt715->sdw_regmap, reg: reg3, |
298 | val: ((val >> 8) & 0xff)); |
299 | if (ret < 0) |
300 | return ret; |
301 | reg4 = reg3 + 0x1000; |
302 | reg4 |= 0x80; |
303 | ret = regmap_write(map: rt715->sdw_regmap, reg: reg4, val: (val & 0xff)); |
304 | if (ret < 0) |
305 | return ret; |
306 | is_index_reg = 1; |
307 | } else if (reg < 0x4fff) { |
308 | ret = regmap_write(map: rt715->sdw_regmap, reg, val); |
309 | if (ret < 0) |
310 | return ret; |
311 | } else if (reg == RT715_FUNC_RESET) { |
312 | ret = regmap_write(map: rt715->sdw_regmap, reg, val); |
313 | if (ret < 0) |
314 | return ret; |
315 | } else if (mask == 0x7000) { |
316 | ret = regmap_write(map: rt715->sdw_regmap, reg, |
317 | val: ((val >> 8) & 0xff)); |
318 | if (ret < 0) |
319 | return ret; |
320 | reg2 = reg + 0x1000; |
321 | reg2 |= 0x80; |
322 | ret = regmap_write(map: rt715->sdw_regmap, reg: reg2, val: (val & 0xff)); |
323 | if (ret < 0) |
324 | return ret; |
325 | } else if ((reg & 0xff00) == 0x8300) { /* for R channel */ |
326 | reg2 = reg - 0x1000; |
327 | reg2 &= ~0x80; |
328 | ret = regmap_write(map: rt715->sdw_regmap, reg: reg2, |
329 | val: ((val >> 8) & 0xff)); |
330 | if (ret < 0) |
331 | return ret; |
332 | ret = regmap_write(map: rt715->sdw_regmap, reg, val: (val & 0xff)); |
333 | if (ret < 0) |
334 | return ret; |
335 | } |
336 | |
337 | if (reg2 == 0) |
338 | dev_dbg(dev, "[%s] %04x <= %04x\n" , __func__, reg, val); |
339 | else if (is_index_reg) |
340 | dev_dbg(dev, "[%s] %04x %04x %04x %04x <= %04x %04x\n" , |
341 | __func__, reg, reg2, reg3, reg4, val2, val); |
342 | else |
343 | dev_dbg(dev, "[%s] %04x %04x <= %04x\n" , |
344 | __func__, reg, reg2, val); |
345 | |
346 | return 0; |
347 | } |
348 | |
349 | static const struct regmap_config rt715_regmap = { |
350 | .reg_bits = 24, |
351 | .val_bits = 32, |
352 | .readable_reg = rt715_readable_register, /* Readable registers */ |
353 | .volatile_reg = rt715_volatile_register, /* volatile register */ |
354 | .max_register = 0x752039, /* Maximum number of register */ |
355 | .reg_defaults = rt715_reg_defaults, /* Defaults */ |
356 | .num_reg_defaults = ARRAY_SIZE(rt715_reg_defaults), |
357 | .cache_type = REGCACHE_MAPLE, |
358 | .use_single_read = true, |
359 | .use_single_write = true, |
360 | .reg_read = rt715_sdw_read, |
361 | .reg_write = rt715_sdw_write, |
362 | }; |
363 | |
364 | static const struct regmap_config rt715_sdw_regmap = { |
365 | .name = "sdw" , |
366 | .reg_bits = 32, /* Total register space for SDW */ |
367 | .val_bits = 8, /* Total number of bits in register */ |
368 | .max_register = 0xff01, /* Maximum number of register */ |
369 | .cache_type = REGCACHE_NONE, |
370 | .use_single_read = true, |
371 | .use_single_write = true, |
372 | }; |
373 | |
374 | int hda_to_sdw(unsigned int nid, unsigned int verb, unsigned int payload, |
375 | unsigned int *sdw_addr_h, unsigned int *sdw_data_h, |
376 | unsigned int *sdw_addr_l, unsigned int *sdw_data_l) |
377 | { |
378 | unsigned int offset_h, offset_l, e_verb; |
379 | |
380 | if (((verb & 0xff) != 0) || verb == 0xf00) { /* 12 bits command */ |
381 | if (verb == 0x7ff) /* special case */ |
382 | offset_h = 0; |
383 | else |
384 | offset_h = 0x3000; |
385 | |
386 | if (verb & 0x800) /* get command */ |
387 | e_verb = (verb - 0xf00) | 0x80; |
388 | else /* set command */ |
389 | e_verb = (verb - 0x700); |
390 | |
391 | *sdw_data_h = payload; /* 7 bits payload */ |
392 | *sdw_addr_l = *sdw_data_l = 0; |
393 | } else { /* 4 bits command */ |
394 | if ((verb & 0x800) == 0x800) { /* read */ |
395 | offset_h = 0x9000; |
396 | offset_l = 0xa000; |
397 | } else { /* write */ |
398 | offset_h = 0x7000; |
399 | offset_l = 0x8000; |
400 | } |
401 | e_verb = verb >> 8; |
402 | *sdw_data_h = (payload >> 8); /* 16 bits payload [15:8] */ |
403 | *sdw_addr_l = (e_verb << 8) | nid | 0x80; /* 0x80: valid bit */ |
404 | *sdw_addr_l += offset_l; |
405 | *sdw_data_l = payload & 0xff; |
406 | } |
407 | |
408 | *sdw_addr_h = (e_verb << 8) | nid; |
409 | *sdw_addr_h += offset_h; |
410 | |
411 | return 0; |
412 | } |
413 | EXPORT_SYMBOL(hda_to_sdw); |
414 | |
415 | static int rt715_update_status(struct sdw_slave *slave, |
416 | enum sdw_slave_status status) |
417 | { |
418 | struct rt715_priv *rt715 = dev_get_drvdata(dev: &slave->dev); |
419 | |
420 | /* |
421 | * Perform initialization only if slave status is present and |
422 | * hw_init flag is false |
423 | */ |
424 | if (rt715->hw_init || status != SDW_SLAVE_ATTACHED) |
425 | return 0; |
426 | |
427 | /* perform I/O transfers required for Slave initialization */ |
428 | return rt715_io_init(dev: &slave->dev, slave); |
429 | } |
430 | |
431 | static int rt715_read_prop(struct sdw_slave *slave) |
432 | { |
433 | struct sdw_slave_prop *prop = &slave->prop; |
434 | int nval, i; |
435 | u32 bit; |
436 | unsigned long addr; |
437 | struct sdw_dpn_prop *dpn; |
438 | |
439 | prop->scp_int1_mask = SDW_SCP_INT1_IMPL_DEF | SDW_SCP_INT1_BUS_CLASH | |
440 | SDW_SCP_INT1_PARITY; |
441 | prop->quirks = SDW_SLAVE_QUIRKS_INVALID_INITIAL_PARITY; |
442 | |
443 | prop->paging_support = false; |
444 | |
445 | /* first we need to allocate memory for set bits in port lists */ |
446 | prop->source_ports = 0x50;/* BITMAP: 01010000 */ |
447 | prop->sink_ports = 0x0; /* BITMAP: 00000000 */ |
448 | |
449 | nval = hweight32(prop->source_ports); |
450 | prop->src_dpn_prop = devm_kcalloc(dev: &slave->dev, n: nval, |
451 | size: sizeof(*prop->src_dpn_prop), |
452 | GFP_KERNEL); |
453 | if (!prop->src_dpn_prop) |
454 | return -ENOMEM; |
455 | |
456 | dpn = prop->src_dpn_prop; |
457 | i = 0; |
458 | addr = prop->source_ports; |
459 | for_each_set_bit(bit, &addr, 32) { |
460 | dpn[i].num = bit; |
461 | dpn[i].simple_ch_prep_sm = true; |
462 | dpn[i].ch_prep_timeout = 10; |
463 | i++; |
464 | } |
465 | |
466 | /* set the timeout values */ |
467 | prop->clk_stop_timeout = 20; |
468 | |
469 | /* wake-up event */ |
470 | prop->wake_capable = 1; |
471 | |
472 | return 0; |
473 | } |
474 | |
475 | static int rt715_bus_config(struct sdw_slave *slave, |
476 | struct sdw_bus_params *params) |
477 | { |
478 | struct rt715_priv *rt715 = dev_get_drvdata(dev: &slave->dev); |
479 | int ret; |
480 | |
481 | memcpy(&rt715->params, params, sizeof(*params)); |
482 | |
483 | ret = rt715_clock_config(dev: &slave->dev); |
484 | if (ret < 0) |
485 | dev_err(&slave->dev, "%s: Invalid clk config" , __func__); |
486 | |
487 | return 0; |
488 | } |
489 | |
490 | static const struct sdw_slave_ops rt715_slave_ops = { |
491 | .read_prop = rt715_read_prop, |
492 | .update_status = rt715_update_status, |
493 | .bus_config = rt715_bus_config, |
494 | }; |
495 | |
496 | static int rt715_sdw_probe(struct sdw_slave *slave, |
497 | const struct sdw_device_id *id) |
498 | { |
499 | struct regmap *sdw_regmap, *regmap; |
500 | |
501 | /* Regmap Initialization */ |
502 | sdw_regmap = devm_regmap_init_sdw(slave, &rt715_sdw_regmap); |
503 | if (IS_ERR(ptr: sdw_regmap)) |
504 | return PTR_ERR(ptr: sdw_regmap); |
505 | |
506 | regmap = devm_regmap_init(&slave->dev, NULL, &slave->dev, |
507 | &rt715_regmap); |
508 | if (IS_ERR(ptr: regmap)) |
509 | return PTR_ERR(ptr: regmap); |
510 | |
511 | return rt715_init(dev: &slave->dev, sdw_regmap, regmap, slave); |
512 | } |
513 | |
514 | static int rt715_sdw_remove(struct sdw_slave *slave) |
515 | { |
516 | pm_runtime_disable(dev: &slave->dev); |
517 | |
518 | return 0; |
519 | } |
520 | |
521 | static const struct sdw_device_id rt715_id[] = { |
522 | SDW_SLAVE_ENTRY_EXT(0x025d, 0x714, 0x2, 0, 0), |
523 | SDW_SLAVE_ENTRY_EXT(0x025d, 0x715, 0x2, 0, 0), |
524 | {}, |
525 | }; |
526 | MODULE_DEVICE_TABLE(sdw, rt715_id); |
527 | |
528 | static int __maybe_unused rt715_dev_suspend(struct device *dev) |
529 | { |
530 | struct rt715_priv *rt715 = dev_get_drvdata(dev); |
531 | |
532 | if (!rt715->hw_init) |
533 | return 0; |
534 | |
535 | regcache_cache_only(map: rt715->regmap, enable: true); |
536 | |
537 | return 0; |
538 | } |
539 | |
540 | #define RT715_PROBE_TIMEOUT 5000 |
541 | |
542 | static int __maybe_unused rt715_dev_resume(struct device *dev) |
543 | { |
544 | struct sdw_slave *slave = dev_to_sdw_dev(dev); |
545 | struct rt715_priv *rt715 = dev_get_drvdata(dev); |
546 | unsigned long time; |
547 | |
548 | if (!rt715->first_hw_init) |
549 | return 0; |
550 | |
551 | if (!slave->unattach_request) |
552 | goto regmap_sync; |
553 | |
554 | time = wait_for_completion_timeout(x: &slave->initialization_complete, |
555 | timeout: msecs_to_jiffies(RT715_PROBE_TIMEOUT)); |
556 | if (!time) { |
557 | dev_err(&slave->dev, "%s: Initialization not complete, timed out\n" , __func__); |
558 | sdw_show_ping_status(bus: slave->bus, sync_delay: true); |
559 | |
560 | return -ETIMEDOUT; |
561 | } |
562 | |
563 | regmap_sync: |
564 | slave->unattach_request = 0; |
565 | regcache_cache_only(map: rt715->regmap, enable: false); |
566 | regcache_sync_region(map: rt715->regmap, min: 0x3000, max: 0x8fff); |
567 | regcache_sync_region(map: rt715->regmap, min: 0x752039, max: 0x752039); |
568 | |
569 | return 0; |
570 | } |
571 | |
572 | static const struct dev_pm_ops rt715_pm = { |
573 | SET_SYSTEM_SLEEP_PM_OPS(rt715_dev_suspend, rt715_dev_resume) |
574 | SET_RUNTIME_PM_OPS(rt715_dev_suspend, rt715_dev_resume, NULL) |
575 | }; |
576 | |
577 | static struct sdw_driver rt715_sdw_driver = { |
578 | .driver = { |
579 | .name = "rt715" , |
580 | .owner = THIS_MODULE, |
581 | .pm = &rt715_pm, |
582 | }, |
583 | .probe = rt715_sdw_probe, |
584 | .remove = rt715_sdw_remove, |
585 | .ops = &rt715_slave_ops, |
586 | .id_table = rt715_id, |
587 | }; |
588 | module_sdw_driver(rt715_sdw_driver); |
589 | |
590 | MODULE_DESCRIPTION("ASoC RT715 driver SDW" ); |
591 | MODULE_AUTHOR("Jack Yu <jack.yu@realtek.com>" ); |
592 | MODULE_LICENSE("GPL v2" ); |
593 | |