e1945e1772cda976a1a9c689c5f36c750e5a3ff2
[sfrench/cifs-2.6.git] / sound / soc / qcom / lpass-platform.c
1 /*
2  * Copyright (c) 2010-2011,2013-2015 The Linux Foundation. All rights reserved.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 and
6  * only version 2 as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11  * GNU General Public License for more details.
12  *
13  * lpass-platform.c -- ALSA SoC platform driver for QTi LPASS
14  */
15
16 #include <linux/dma-mapping.h>
17 #include <linux/export.h>
18 #include <linux/kernel.h>
19 #include <linux/module.h>
20 #include <linux/platform_device.h>
21 #include <sound/pcm_params.h>
22 #include <linux/regmap.h>
23 #include <sound/soc.h>
24 #include "lpass-lpaif-reg.h"
25 #include "lpass.h"
26
27 struct lpass_pcm_data {
28         int dma_ch;
29         int i2s_port;
30 };
31
32 #define LPASS_PLATFORM_BUFFER_SIZE      (16 * 1024)
33 #define LPASS_PLATFORM_PERIODS          2
34
35 static const struct snd_pcm_hardware lpass_platform_pcm_hardware = {
36         .info                   =       SNDRV_PCM_INFO_MMAP |
37                                         SNDRV_PCM_INFO_MMAP_VALID |
38                                         SNDRV_PCM_INFO_INTERLEAVED |
39                                         SNDRV_PCM_INFO_PAUSE |
40                                         SNDRV_PCM_INFO_RESUME,
41         .formats                =       SNDRV_PCM_FMTBIT_S16 |
42                                         SNDRV_PCM_FMTBIT_S24 |
43                                         SNDRV_PCM_FMTBIT_S32,
44         .rates                  =       SNDRV_PCM_RATE_8000_192000,
45         .rate_min               =       8000,
46         .rate_max               =       192000,
47         .channels_min           =       1,
48         .channels_max           =       8,
49         .buffer_bytes_max       =       LPASS_PLATFORM_BUFFER_SIZE,
50         .period_bytes_max       =       LPASS_PLATFORM_BUFFER_SIZE /
51                                                 LPASS_PLATFORM_PERIODS,
52         .period_bytes_min       =       LPASS_PLATFORM_BUFFER_SIZE /
53                                                 LPASS_PLATFORM_PERIODS,
54         .periods_min            =       LPASS_PLATFORM_PERIODS,
55         .periods_max            =       LPASS_PLATFORM_PERIODS,
56         .fifo_size              =       0,
57 };
58
59 static int lpass_platform_pcmops_open(struct snd_pcm_substream *substream)
60 {
61         struct snd_pcm_runtime *runtime = substream->runtime;
62         struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
63         struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
64         struct lpass_data *drvdata =
65                 snd_soc_platform_get_drvdata(soc_runtime->platform);
66         struct lpass_variant *v = drvdata->variant;
67         int ret, dma_ch, dir = substream->stream;
68         struct lpass_pcm_data *data;
69
70         data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL);
71         if (!data)
72                 return -ENOMEM;
73
74         data->i2s_port = cpu_dai->driver->id;
75         runtime->private_data = data;
76
77         dma_ch = 0;
78         if (v->alloc_dma_channel)
79                 dma_ch = v->alloc_dma_channel(drvdata, dir);
80         else
81                 dma_ch = 0;
82
83         if (dma_ch < 0)
84                 return dma_ch;
85
86         drvdata->substream[dma_ch] = substream;
87
88         ret = regmap_write(drvdata->lpaif_map,
89                         LPAIF_DMACTL_REG(v, dma_ch, dir), 0);
90         if (ret) {
91                 dev_err(soc_runtime->dev,
92                         "error writing to rdmactl reg: %d\n", ret);
93                         return ret;
94         }
95
96         data->dma_ch = dma_ch;
97
98         snd_soc_set_runtime_hwparams(substream, &lpass_platform_pcm_hardware);
99
100         runtime->dma_bytes = lpass_platform_pcm_hardware.buffer_bytes_max;
101
102         ret = snd_pcm_hw_constraint_integer(runtime,
103                         SNDRV_PCM_HW_PARAM_PERIODS);
104         if (ret < 0) {
105                 dev_err(soc_runtime->dev, "setting constraints failed: %d\n",
106                         ret);
107                 return -EINVAL;
108         }
109
110         snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
111
112         return 0;
113 }
114
115 static int lpass_platform_pcmops_close(struct snd_pcm_substream *substream)
116 {
117         struct snd_pcm_runtime *runtime = substream->runtime;
118         struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
119         struct lpass_data *drvdata =
120                 snd_soc_platform_get_drvdata(soc_runtime->platform);
121         struct lpass_variant *v = drvdata->variant;
122         struct lpass_pcm_data *data;
123
124         data = runtime->private_data;
125         v = drvdata->variant;
126         drvdata->substream[data->dma_ch] = NULL;
127         if (v->free_dma_channel)
128                 v->free_dma_channel(drvdata, data->dma_ch);
129
130         return 0;
131 }
132
133 static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
134                 struct snd_pcm_hw_params *params)
135 {
136         struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
137         struct lpass_data *drvdata =
138                 snd_soc_platform_get_drvdata(soc_runtime->platform);
139         struct snd_pcm_runtime *rt = substream->runtime;
140         struct lpass_pcm_data *pcm_data = rt->private_data;
141         struct lpass_variant *v = drvdata->variant;
142         snd_pcm_format_t format = params_format(params);
143         unsigned int channels = params_channels(params);
144         unsigned int regval;
145         int ch, dir = substream->stream;
146         int bitwidth;
147         int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start;
148
149         ch = pcm_data->dma_ch;
150
151         bitwidth = snd_pcm_format_width(format);
152         if (bitwidth < 0) {
153                 dev_err(soc_runtime->dev, "invalid bit width given: %d\n",
154                                 bitwidth);
155                 return bitwidth;
156         }
157
158         regval = LPAIF_DMACTL_BURSTEN_INCR4 |
159                         LPAIF_DMACTL_AUDINTF(dma_port) |
160                         LPAIF_DMACTL_FIFOWM_8;
161
162         switch (bitwidth) {
163         case 16:
164                 switch (channels) {
165                 case 1:
166                 case 2:
167                         regval |= LPAIF_DMACTL_WPSCNT_ONE;
168                         break;
169                 case 4:
170                         regval |= LPAIF_DMACTL_WPSCNT_TWO;
171                         break;
172                 case 6:
173                         regval |= LPAIF_DMACTL_WPSCNT_THREE;
174                         break;
175                 case 8:
176                         regval |= LPAIF_DMACTL_WPSCNT_FOUR;
177                         break;
178                 default:
179                         dev_err(soc_runtime->dev,
180                                 "invalid PCM config given: bw=%d, ch=%u\n",
181                                 bitwidth, channels);
182                         return -EINVAL;
183                 }
184                 break;
185         case 24:
186         case 32:
187                 switch (channels) {
188                 case 1:
189                         regval |= LPAIF_DMACTL_WPSCNT_ONE;
190                         break;
191                 case 2:
192                         regval |= LPAIF_DMACTL_WPSCNT_TWO;
193                         break;
194                 case 4:
195                         regval |= LPAIF_DMACTL_WPSCNT_FOUR;
196                         break;
197                 case 6:
198                         regval |= LPAIF_DMACTL_WPSCNT_SIX;
199                         break;
200                 case 8:
201                         regval |= LPAIF_DMACTL_WPSCNT_EIGHT;
202                         break;
203                 default:
204                         dev_err(soc_runtime->dev,
205                                 "invalid PCM config given: bw=%d, ch=%u\n",
206                                 bitwidth, channels);
207                         return -EINVAL;
208                 }
209                 break;
210         default:
211                 dev_err(soc_runtime->dev, "invalid PCM config given: bw=%d, ch=%u\n",
212                         bitwidth, channels);
213                 return -EINVAL;
214         }
215
216         ret = regmap_write(drvdata->lpaif_map,
217                         LPAIF_DMACTL_REG(v, ch, dir), regval);
218         if (ret) {
219                 dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
220                         ret);
221                 return ret;
222         }
223
224         return 0;
225 }
226
227 static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
228 {
229         struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
230         struct lpass_data *drvdata =
231                 snd_soc_platform_get_drvdata(soc_runtime->platform);
232         struct snd_pcm_runtime *rt = substream->runtime;
233         struct lpass_pcm_data *pcm_data = rt->private_data;
234         struct lpass_variant *v = drvdata->variant;
235         unsigned int reg;
236         int ret;
237
238         reg = LPAIF_DMACTL_REG(v, pcm_data->dma_ch, substream->stream);
239         ret = regmap_write(drvdata->lpaif_map, reg, 0);
240         if (ret)
241                 dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
242                         ret);
243
244         return ret;
245 }
246
247 static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
248 {
249         struct snd_pcm_runtime *runtime = substream->runtime;
250         struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
251         struct lpass_data *drvdata =
252                 snd_soc_platform_get_drvdata(soc_runtime->platform);
253         struct snd_pcm_runtime *rt = substream->runtime;
254         struct lpass_pcm_data *pcm_data = rt->private_data;
255         struct lpass_variant *v = drvdata->variant;
256         int ret, ch, dir = substream->stream;
257
258         ch = pcm_data->dma_ch;
259
260         ret = regmap_write(drvdata->lpaif_map,
261                         LPAIF_DMABASE_REG(v, ch, dir),
262                         runtime->dma_addr);
263         if (ret) {
264                 dev_err(soc_runtime->dev, "error writing to rdmabase reg: %d\n",
265                         ret);
266                 return ret;
267         }
268
269         ret = regmap_write(drvdata->lpaif_map,
270                         LPAIF_DMABUFF_REG(v, ch, dir),
271                         (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
272         if (ret) {
273                 dev_err(soc_runtime->dev, "error writing to rdmabuff reg: %d\n",
274                         ret);
275                 return ret;
276         }
277
278         ret = regmap_write(drvdata->lpaif_map,
279                         LPAIF_DMAPER_REG(v, ch, dir),
280                         (snd_pcm_lib_period_bytes(substream) >> 2) - 1);
281         if (ret) {
282                 dev_err(soc_runtime->dev, "error writing to rdmaper reg: %d\n",
283                         ret);
284                 return ret;
285         }
286
287         ret = regmap_update_bits(drvdata->lpaif_map,
288                         LPAIF_DMACTL_REG(v, ch, dir),
289                         LPAIF_DMACTL_ENABLE_MASK, LPAIF_DMACTL_ENABLE_ON);
290         if (ret) {
291                 dev_err(soc_runtime->dev, "error writing to rdmactl reg: %d\n",
292                         ret);
293                 return ret;
294         }
295
296         return 0;
297 }
298
299 static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
300                 int cmd)
301 {
302         struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
303         struct lpass_data *drvdata =
304                 snd_soc_platform_get_drvdata(soc_runtime->platform);
305         struct snd_pcm_runtime *rt = substream->runtime;
306         struct lpass_pcm_data *pcm_data = rt->private_data;
307         struct lpass_variant *v = drvdata->variant;
308         int ret, ch, dir = substream->stream;
309
310         ch = pcm_data->dma_ch;
311
312         switch (cmd) {
313         case SNDRV_PCM_TRIGGER_START:
314         case SNDRV_PCM_TRIGGER_RESUME:
315         case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
316                 /* clear status before enabling interrupts */
317                 ret = regmap_write(drvdata->lpaif_map,
318                                 LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
319                                 LPAIF_IRQ_ALL(ch));
320                 if (ret) {
321                         dev_err(soc_runtime->dev,
322                                 "error writing to irqclear reg: %d\n", ret);
323                         return ret;
324                 }
325
326                 ret = regmap_update_bits(drvdata->lpaif_map,
327                                 LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
328                                 LPAIF_IRQ_ALL(ch),
329                                 LPAIF_IRQ_ALL(ch));
330                 if (ret) {
331                         dev_err(soc_runtime->dev,
332                                 "error writing to irqen reg: %d\n", ret);
333                         return ret;
334                 }
335
336                 ret = regmap_update_bits(drvdata->lpaif_map,
337                                 LPAIF_DMACTL_REG(v, ch, dir),
338                                 LPAIF_DMACTL_ENABLE_MASK,
339                                 LPAIF_DMACTL_ENABLE_ON);
340                 if (ret) {
341                         dev_err(soc_runtime->dev,
342                                 "error writing to rdmactl reg: %d\n", ret);
343                         return ret;
344                 }
345                 break;
346         case SNDRV_PCM_TRIGGER_STOP:
347         case SNDRV_PCM_TRIGGER_SUSPEND:
348         case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
349                 ret = regmap_update_bits(drvdata->lpaif_map,
350                                 LPAIF_DMACTL_REG(v, ch, dir),
351                                 LPAIF_DMACTL_ENABLE_MASK,
352                                 LPAIF_DMACTL_ENABLE_OFF);
353                 if (ret) {
354                         dev_err(soc_runtime->dev,
355                                 "error writing to rdmactl reg: %d\n", ret);
356                         return ret;
357                 }
358
359                 ret = regmap_update_bits(drvdata->lpaif_map,
360                                 LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST),
361                                 LPAIF_IRQ_ALL(ch), 0);
362                 if (ret) {
363                         dev_err(soc_runtime->dev,
364                                 "error writing to irqen reg: %d\n", ret);
365                         return ret;
366                 }
367                 break;
368         }
369
370         return 0;
371 }
372
373 static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
374                 struct snd_pcm_substream *substream)
375 {
376         struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
377         struct lpass_data *drvdata =
378                         snd_soc_platform_get_drvdata(soc_runtime->platform);
379         struct snd_pcm_runtime *rt = substream->runtime;
380         struct lpass_pcm_data *pcm_data = rt->private_data;
381         struct lpass_variant *v = drvdata->variant;
382         unsigned int base_addr, curr_addr;
383         int ret, ch, dir = substream->stream;
384
385         ch = pcm_data->dma_ch;
386
387         ret = regmap_read(drvdata->lpaif_map,
388                         LPAIF_DMABASE_REG(v, ch, dir), &base_addr);
389         if (ret) {
390                 dev_err(soc_runtime->dev,
391                         "error reading from rdmabase reg: %d\n", ret);
392                 return ret;
393         }
394
395         ret = regmap_read(drvdata->lpaif_map,
396                         LPAIF_DMACURR_REG(v, ch, dir), &curr_addr);
397         if (ret) {
398                 dev_err(soc_runtime->dev,
399                         "error reading from rdmacurr reg: %d\n", ret);
400                 return ret;
401         }
402
403         return bytes_to_frames(substream->runtime, curr_addr - base_addr);
404 }
405
406 static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream,
407                 struct vm_area_struct *vma)
408 {
409         struct snd_pcm_runtime *runtime = substream->runtime;
410
411         return dma_mmap_coherent(substream->pcm->card->dev, vma,
412                         runtime->dma_area, runtime->dma_addr,
413                         runtime->dma_bytes);
414 }
415
416 static const struct snd_pcm_ops lpass_platform_pcm_ops = {
417         .open           = lpass_platform_pcmops_open,
418         .close          = lpass_platform_pcmops_close,
419         .ioctl          = snd_pcm_lib_ioctl,
420         .hw_params      = lpass_platform_pcmops_hw_params,
421         .hw_free        = lpass_platform_pcmops_hw_free,
422         .prepare        = lpass_platform_pcmops_prepare,
423         .trigger        = lpass_platform_pcmops_trigger,
424         .pointer        = lpass_platform_pcmops_pointer,
425         .mmap           = lpass_platform_pcmops_mmap,
426 };
427
428 static irqreturn_t lpass_dma_interrupt_handler(
429                         struct snd_pcm_substream *substream,
430                         struct lpass_data *drvdata,
431                         int chan, u32 interrupts)
432 {
433         struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
434         struct lpass_variant *v = drvdata->variant;
435         irqreturn_t ret = IRQ_NONE;
436         int rv;
437
438         if (interrupts & LPAIF_IRQ_PER(chan)) {
439                 rv = regmap_write(drvdata->lpaif_map,
440                                 LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
441                                 LPAIF_IRQ_PER(chan));
442                 if (rv) {
443                         dev_err(soc_runtime->dev,
444                                 "error writing to irqclear reg: %d\n", rv);
445                         return IRQ_NONE;
446                 }
447                 snd_pcm_period_elapsed(substream);
448                 ret = IRQ_HANDLED;
449         }
450
451         if (interrupts & LPAIF_IRQ_XRUN(chan)) {
452                 rv = regmap_write(drvdata->lpaif_map,
453                                 LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
454                                 LPAIF_IRQ_XRUN(chan));
455                 if (rv) {
456                         dev_err(soc_runtime->dev,
457                                 "error writing to irqclear reg: %d\n", rv);
458                         return IRQ_NONE;
459                 }
460                 dev_warn(soc_runtime->dev, "xrun warning\n");
461                 snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
462                 ret = IRQ_HANDLED;
463         }
464
465         if (interrupts & LPAIF_IRQ_ERR(chan)) {
466                 rv = regmap_write(drvdata->lpaif_map,
467                                 LPAIF_IRQCLEAR_REG(v, LPAIF_IRQ_PORT_HOST),
468                                 LPAIF_IRQ_ERR(chan));
469                 if (rv) {
470                         dev_err(soc_runtime->dev,
471                                 "error writing to irqclear reg: %d\n", rv);
472                         return IRQ_NONE;
473                 }
474                 dev_err(soc_runtime->dev, "bus access error\n");
475                 snd_pcm_stop(substream, SNDRV_PCM_STATE_DISCONNECTED);
476                 ret = IRQ_HANDLED;
477         }
478
479         return ret;
480 }
481
482 static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
483 {
484         struct lpass_data *drvdata = data;
485         struct lpass_variant *v = drvdata->variant;
486         unsigned int irqs;
487         int rv, chan;
488
489         rv = regmap_read(drvdata->lpaif_map,
490                         LPAIF_IRQSTAT_REG(v, LPAIF_IRQ_PORT_HOST), &irqs);
491         if (rv) {
492                 pr_err("error reading from irqstat reg: %d\n", rv);
493                 return IRQ_NONE;
494         }
495
496         /* Handle per channel interrupts */
497         for (chan = 0; chan < LPASS_MAX_DMA_CHANNELS; chan++) {
498                 if (irqs & LPAIF_IRQ_ALL(chan) && drvdata->substream[chan]) {
499                         rv = lpass_dma_interrupt_handler(
500                                                 drvdata->substream[chan],
501                                                 drvdata, chan, irqs);
502                         if (rv != IRQ_HANDLED)
503                                 return rv;
504                 }
505         }
506
507         return IRQ_HANDLED;
508 }
509
510 static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
511 {
512         struct snd_pcm *pcm = soc_runtime->pcm;
513         struct snd_pcm_substream *psubstream, *csubstream;
514         int ret = -EINVAL;
515         size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
516
517         psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
518         if (psubstream) {
519                 ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
520                                         soc_runtime->platform->dev,
521                                         size, &psubstream->dma_buffer);
522                 if (ret) {
523                         dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
524                         return ret;
525                 }
526         }
527
528         csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
529         if (csubstream) {
530                 ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
531                                         soc_runtime->platform->dev,
532                                         size, &csubstream->dma_buffer);
533                 if (ret) {
534                         dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
535                         if (psubstream)
536                                 snd_dma_free_pages(&psubstream->dma_buffer);
537                         return ret;
538                 }
539
540         }
541
542         return 0;
543 }
544
545 static void lpass_platform_pcm_free(struct snd_pcm *pcm)
546 {
547         struct snd_pcm_substream *substream;
548         int i;
549
550         for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) {
551                 substream = pcm->streams[i].substream;
552                 if (substream) {
553                         snd_dma_free_pages(&substream->dma_buffer);
554                         substream->dma_buffer.area = NULL;
555                         substream->dma_buffer.addr = 0;
556                 }
557         }
558 }
559
560 static const struct snd_soc_platform_driver lpass_platform_driver = {
561         .pcm_new        = lpass_platform_pcm_new,
562         .pcm_free       = lpass_platform_pcm_free,
563         .ops            = &lpass_platform_pcm_ops,
564 };
565
566 int asoc_qcom_lpass_platform_register(struct platform_device *pdev)
567 {
568         struct lpass_data *drvdata = platform_get_drvdata(pdev);
569         struct lpass_variant *v = drvdata->variant;
570         int ret;
571
572         drvdata->lpaif_irq = platform_get_irq_byname(pdev, "lpass-irq-lpaif");
573         if (drvdata->lpaif_irq < 0) {
574                 dev_err(&pdev->dev, "error getting irq handle: %d\n",
575                         drvdata->lpaif_irq);
576                 return -ENODEV;
577         }
578
579         /* ensure audio hardware is disabled */
580         ret = regmap_write(drvdata->lpaif_map,
581                         LPAIF_IRQEN_REG(v, LPAIF_IRQ_PORT_HOST), 0);
582         if (ret) {
583                 dev_err(&pdev->dev, "error writing to irqen reg: %d\n", ret);
584                 return ret;
585         }
586
587         ret = devm_request_irq(&pdev->dev, drvdata->lpaif_irq,
588                         lpass_platform_lpaif_irq, IRQF_TRIGGER_RISING,
589                         "lpass-irq-lpaif", drvdata);
590         if (ret) {
591                 dev_err(&pdev->dev, "irq request failed: %d\n", ret);
592                 return ret;
593         }
594
595
596         return devm_snd_soc_register_platform(&pdev->dev,
597                         &lpass_platform_driver);
598 }
599 EXPORT_SYMBOL_GPL(asoc_qcom_lpass_platform_register);
600
601 MODULE_DESCRIPTION("QTi LPASS Platform Driver");
602 MODULE_LICENSE("GPL v2");