534f00d01248f48c32263506389d39d10059bcc2
[firefly-linux-kernel-4.4.55.git] / drivers / video / rockchip / display-sys.c
1 #include <linux/module.h>
2 #include <linux/ctype.h>
3 #include <linux/idr.h>
4 #include <linux/err.h>
5 #include <linux/kdev_t.h>
6 #include <linux/display-sys.h>
7
8 static struct list_head main_display_device_list;
9 static struct list_head aux_display_device_list;
10
11 static ssize_t display_show_name(struct device *dev,
12                                  struct device_attribute *attr, char *buf)
13 {
14         struct rk_display_device *dsp = dev_get_drvdata(dev);
15
16         return snprintf(buf, PAGE_SIZE, "%s\n", dsp->name);
17 }
18
19 static ssize_t display_show_type(struct device *dev,
20                                  struct device_attribute *attr, char *buf)
21 {
22         struct rk_display_device *dsp = dev_get_drvdata(dev);
23
24         return snprintf(buf, PAGE_SIZE, "%s\n", dsp->type);
25 }
26
27 static ssize_t display_show_property(struct device *dev,
28                                      struct device_attribute *attr, char *buf)
29 {
30         struct rk_display_device *dsp = dev_get_drvdata(dev);
31
32         return snprintf(buf, PAGE_SIZE, "%d\n", dsp->property);
33 }
34
35 static ssize_t display_show_enable(struct device *dev,
36                                    struct device_attribute *attr, char *buf)
37 {
38         struct rk_display_device *dsp = dev_get_drvdata(dev);
39         int enable;
40
41         if (dsp->ops && dsp->ops->getenable)
42                 enable = dsp->ops->getenable(dsp);
43         else
44                 return 0;
45         return snprintf(buf, PAGE_SIZE, "%d\n", enable);
46 }
47
48 static ssize_t display_store_enable(struct device *dev,
49                                     struct device_attribute *attr,
50                                     const char *buf, size_t size)
51 {
52         struct rk_display_device *dsp = dev_get_drvdata(dev);
53         int enable;
54
55         if (kstrtoint(buf, 0, &enable))
56                 return size;
57         if (dsp->ops && dsp->ops->setenable)
58                 dsp->ops->setenable(dsp, enable);
59         return size;
60 }
61
62 static ssize_t display_show_connect(struct device *dev,
63                                     struct device_attribute *attr, char *buf)
64 {
65         struct rk_display_device *dsp = dev_get_drvdata(dev);
66         int connect;
67
68         if (dsp->ops && dsp->ops->getstatus)
69                 connect = dsp->ops->getstatus(dsp);
70         else
71                 return 0;
72         return snprintf(buf, PAGE_SIZE, "%d\n", connect);
73 }
74
75 static int mode_string(char *buf, unsigned int offset,
76                        const struct fb_videomode *mode)
77 {
78         char v = 'p';
79
80         if (!buf || !mode) {
81                 pr_err("%s parameter error\n", __func__);
82                 return 0;
83         }
84         if (mode->xres == 0 && mode->yres == 0)
85                 return snprintf(&buf[offset], PAGE_SIZE - offset, "auto\n");
86 /*
87         if (mode->flag & FB_MODE_IS_DETAILED)
88                 m = 'D';
89         if (mode->flag & FB_MODE_IS_VESA)
90                 m = 'V';
91         if (mode->flag & FB_MODE_IS_STANDARD)
92                 m = 'S';
93 */
94         if (mode->vmode & FB_VMODE_INTERLACED)
95                 v = 'i';
96         if (mode->vmode & FB_VMODE_DOUBLE)
97                 v = 'd';
98         if (mode->flag)
99                 return snprintf(&buf[offset], PAGE_SIZE - offset,
100                                 "%dx%d%c-%d(YCbCr420)\n",
101                                 mode->xres, mode->yres, v, mode->refresh);
102         else
103                 return snprintf(&buf[offset], PAGE_SIZE - offset,
104                                 "%dx%d%c-%d\n",
105                                 mode->xres, mode->yres, v, mode->refresh);
106 }
107
108 static ssize_t display_show_modes(struct device *dev,
109                                   struct device_attribute *attr, char *buf)
110 {
111         struct rk_display_device *dsp = dev_get_drvdata(dev);
112         struct list_head *modelist, *pos;
113         struct display_modelist *display_modelist;
114         const struct fb_videomode *mode;
115         int i;
116
117         mutex_lock(&dsp->lock);
118         if (dsp->ops && dsp->ops->getmodelist) {
119                 if (dsp->ops->getmodelist(dsp, &modelist)) {
120                         mutex_unlock(&dsp->lock);
121                         return -EINVAL;
122                 }
123         } else {
124                 mutex_unlock(&dsp->lock);
125                 return 0;
126         }
127         i = 0;
128         if (dsp->priority == DISPLAY_PRIORITY_HDMI)
129                 i += snprintf(buf, PAGE_SIZE, "auto\n");
130
131         list_for_each(pos, modelist) {
132                 display_modelist = list_entry(pos,
133                                               struct display_modelist,
134                                               list);
135                 mode = &display_modelist->mode;
136                 i += mode_string(buf, i, mode);
137         }
138         mutex_unlock(&dsp->lock);
139         return i;
140 }
141
142 static ssize_t display_show_mode(struct device *dev,
143                                  struct device_attribute *attr, char *buf)
144 {
145         struct rk_display_device *dsp = dev_get_drvdata(dev);
146         struct fb_videomode mode;
147
148         if (dsp->ops && dsp->ops->getmode)
149                 if (dsp->ops->getmode(dsp, &mode) == 0)
150                         return mode_string(buf, 0, &mode);
151         return 0;
152 }
153
154 static ssize_t display_store_mode(struct device *dev,
155                                   struct device_attribute *attr,
156                                   const char *buf, size_t count)
157 {
158         struct rk_display_device *dsp = dev_get_drvdata(dev);
159         char mstr[100];
160         struct list_head *modelist, *pos;
161         struct display_modelist *display_modelist;
162         struct fb_videomode *mode;
163         size_t i;
164
165         if (!memcmp(buf, "auto", 4)) {
166                 if (dsp->ops && dsp->ops->setmode)
167                         dsp->ops->setmode(dsp, NULL);
168                 return count;
169         }
170
171         if (dsp->ops && dsp->ops->getmodelist) {
172                 if (dsp->ops && dsp->ops->getmodelist) {
173                         if (dsp->ops->getmodelist(dsp, &modelist))
174                                 return -EINVAL;
175                 }
176                 list_for_each(pos, modelist) {
177                         display_modelist = list_entry(pos,
178                                                       struct display_modelist,
179                                                       list);
180                         mode = &display_modelist->mode;
181                         i = mode_string(mstr, 0, mode);
182                         if (strncmp(mstr, buf, max(count, i)) == 0) {
183                                 if (dsp->ops && dsp->ops->setmode)
184                                         dsp->ops->setmode(dsp, mode);
185                                 return count;
186                         }
187                 }
188         }
189         return -EINVAL;
190 }
191
192 static ssize_t display_show_scale(struct device *dev,
193                                   struct device_attribute *attr,
194                                   char *buf)
195 {
196         struct rk_display_device *dsp = dev_get_drvdata(dev);
197         int xscale, yscale;
198
199         if (dsp->ops && dsp->ops->getscale) {
200                 xscale = dsp->ops->getscale(dsp, DISPLAY_SCALE_X);
201                 yscale = dsp->ops->getscale(dsp, DISPLAY_SCALE_Y);
202                 if (xscale && yscale)
203                         return snprintf(buf, PAGE_SIZE,
204                                         "xscale=%d yscale=%d\n",
205                                         xscale, yscale);
206         }
207         return -EINVAL;
208 }
209
210 static ssize_t display_store_scale(struct device *dev,
211                                    struct device_attribute *attr,
212                                    const char *buf, size_t count)
213 {
214         struct rk_display_device *dsp = dev_get_drvdata(dev);
215         int scale = 100;
216
217         if (dsp->ops && dsp->ops->setscale) {
218                 if (!strncmp(buf, "xscale", 6)) {
219                         if (!kstrtoint(buf, 0, &scale))
220                                 dsp->ops->setscale(dsp,
221                                                    DISPLAY_SCALE_X,
222                                                    scale);
223                 } else if (!strncmp(buf, "yscale", 6)) {
224                         if (!kstrtoint(buf, 0, &scale))
225                                 dsp->ops->setscale(dsp,
226                                                    DISPLAY_SCALE_Y,
227                                                    scale);
228                 } else {
229                         if (!kstrtoint(buf, 0, &scale)) {
230                                 dsp->ops->setscale(dsp,
231                                                    DISPLAY_SCALE_X,
232                                                    scale);
233                                 dsp->ops->setscale(dsp,
234                                                    DISPLAY_SCALE_Y,
235                                                    scale);
236                         }
237                 }
238                 return count;
239         }
240         return -EINVAL;
241 }
242
243 static ssize_t display_show_3dmode(struct device *dev,
244                                    struct device_attribute *attr, char *buf)
245 {
246         struct rk_display_device *dsp = dev_get_drvdata(dev);
247         struct list_head *modelist, *pos;
248         struct display_modelist *display_modelist;
249         struct fb_videomode mode;
250         int i = 0, cur_3d_mode = -1;
251         char mode_str[128];
252         int mode_strlen, format_3d;
253
254         if (dsp->ops && dsp->ops->getmodelist) {
255                 if (dsp->ops->getmodelist(dsp, &modelist))
256                         return -EINVAL;
257         } else {
258                 return 0;
259         }
260
261         if (dsp->ops && dsp->ops->getmode) {
262                 if (dsp->ops->getmode(dsp, &mode))
263                         return -EINVAL;
264         } else {
265                 return 0;
266         }
267
268         list_for_each(pos, modelist) {
269                 display_modelist = list_entry(pos,
270                                               struct display_modelist,
271                                               list);
272                 if (fb_mode_is_equal(&mode, &display_modelist->mode))
273                         break;
274                 else
275                         display_modelist = NULL;
276         }
277         if (display_modelist)
278                 i = snprintf(buf, PAGE_SIZE, "3dmodes=%d\n",
279                              display_modelist->format_3d);
280         else
281                 i = snprintf(buf, PAGE_SIZE, "3dmodes=0\n");
282
283         if (dsp->ops && dsp->ops->get3dmode)
284                 cur_3d_mode = dsp->ops->get3dmode(dsp);
285         i += snprintf(buf + i, PAGE_SIZE - i, "cur3dmode=%d\n", cur_3d_mode);
286
287         list_for_each(pos, modelist) {
288                 display_modelist = list_entry(pos,
289                                               struct display_modelist,
290                                               list);
291                 mode_strlen = mode_string(mode_str, 0,
292                                           &(display_modelist->mode));
293                 mode_str[mode_strlen-1] = 0;
294                 format_3d = display_modelist->format_3d;
295                 i += snprintf(buf+i, PAGE_SIZE, "%s,%d\n",
296                               mode_str, format_3d);
297         }
298         return i;
299 }
300
301 static ssize_t display_store_3dmode(struct device *dev,
302                                     struct device_attribute *attr,
303                                     const char *buf, size_t count)
304 {
305         struct rk_display_device *dsp = dev_get_drvdata(dev);
306         int mode;
307
308         if (dsp->ops && dsp->ops->set3dmode) {
309                 if (!kstrtoint(buf, 0, &mode))
310                         dsp->ops->set3dmode(dsp, mode);
311                 return count;
312         }
313         return -EINVAL;
314 }
315
316 static ssize_t display_show_color(struct device *dev,
317                                   struct device_attribute *attr, char *buf)
318 {
319         struct rk_display_device *dsp = dev_get_drvdata(dev);
320
321         if (dsp->ops && dsp->ops->getcolor)
322                 return dsp->ops->getcolor(dsp, buf);
323         else
324                 return 0;
325 }
326
327 static ssize_t display_store_color(struct device *dev,
328                                    struct device_attribute *attr,
329                                    const char *buf, size_t count)
330 {
331         struct rk_display_device *dsp = dev_get_drvdata(dev);
332
333         if (dsp->ops && dsp->ops->setcolor) {
334                 if (!dsp->ops->setcolor(dsp, buf, count))
335                         return count;
336         }
337         return -EINVAL;
338 }
339
340 static ssize_t display_show_sinkaudioinfo(struct device *dev,
341                                           struct device_attribute *attr,
342                                           char *buf)
343 {
344         struct rk_display_device *dsp = dev_get_drvdata(dev);
345         char audioinfo[200];
346         int ret = 0;
347
348         if (dsp->ops && dsp->ops->getedidaudioinfo) {
349                 ret = dsp->ops->getedidaudioinfo(dsp, audioinfo, 200);
350                 if (!ret)
351                         return snprintf(buf, PAGE_SIZE, "%s\n", audioinfo);
352         }
353         return -EINVAL;
354 }
355
356 static ssize_t display_show_monspecs(struct device *dev,
357                                      struct device_attribute *attr, char *buf)
358 {
359         struct rk_display_device *dsp = dev_get_drvdata(dev);
360         struct fb_monspecs monspecs;
361         int ret = 0;
362
363         if (dsp->ops && dsp->ops->getmonspecs) {
364                 ret = dsp->ops->getmonspecs(dsp, &monspecs);
365                 if (!ret) {
366                         memcpy(buf, &monspecs, sizeof(struct fb_monspecs));
367                         return sizeof(struct fb_monspecs);
368                 }
369         }
370         return -EINVAL;
371 }
372
373 static ssize_t display_show_debug(struct device *dev,
374                                   struct device_attribute *attr, char *buf)
375 {
376         struct rk_display_device *dsp = dev_get_drvdata(dev);
377
378         if (dsp->ops && dsp->ops->getdebug)
379                 return dsp->ops->getdebug(dsp, buf);
380         else
381                 return -EINVAL;
382 }
383
384 static ssize_t display_store_debug(struct device *dev,
385                                    struct device_attribute *attr,
386                                    const char *buf, size_t count)
387 {
388         int cmd;
389         struct rk_display_device *dsp = dev_get_drvdata(dev);
390
391         if (dsp->ops && dsp->ops->setdebug) {
392                 if (kstrtoint(buf, 0, &cmd) == 0)
393                         dsp->ops->setdebug(dsp, cmd);
394                 return count;
395         }
396         return -EINVAL;
397 }
398
399 static struct device_attribute display_attrs[] = {
400         __ATTR(name, S_IRUGO, display_show_name, NULL),
401         __ATTR(type, S_IRUGO, display_show_type, NULL),
402         __ATTR(property, S_IRUGO, display_show_property, NULL),
403         __ATTR(enable, 0666, display_show_enable, display_store_enable),
404         __ATTR(connect, S_IRUGO, display_show_connect, NULL),
405         __ATTR(modes, S_IRUGO, display_show_modes, NULL),
406         __ATTR(mode, 0666, display_show_mode, display_store_mode),
407         __ATTR(scale, 0666, display_show_scale, display_store_scale),
408         __ATTR(3dmode, 0666, display_show_3dmode, display_store_3dmode),
409         __ATTR(color, 0666, display_show_color, display_store_color),
410         __ATTR(audioinfo, S_IRUGO, display_show_sinkaudioinfo, NULL),
411         __ATTR(monspecs, S_IRUGO, display_show_monspecs, NULL),
412         __ATTR(debug, 0664, display_show_debug, display_store_debug),
413         __ATTR_NULL
414 };
415
416 static int display_suspend(struct device *dev, pm_message_t state)
417 {
418         struct rk_display_device *dsp = dev_get_drvdata(dev);
419
420         mutex_lock(&dsp->lock);
421         if (likely(dsp->driver->suspend))
422                 dsp->driver->suspend(dsp, state);
423         mutex_unlock(&dsp->lock);
424         return 0;
425 };
426
427 static int display_resume(struct device *dev)
428 {
429         struct rk_display_device *dsp = dev_get_drvdata(dev);
430
431         mutex_lock(&dsp->lock);
432         if (likely(dsp->driver->resume))
433                 dsp->driver->resume(dsp);
434         mutex_unlock(&dsp->lock);
435         return 0;
436 };
437
438 int display_add_videomode(const struct fb_videomode *mode,
439                           struct list_head *head)
440 {
441         struct list_head *pos;
442         struct display_modelist *modelist;
443         struct fb_videomode *m;
444         int found = 0;
445
446         list_for_each(pos, head) {
447                 modelist = list_entry(pos, struct display_modelist, list);
448                 m = &modelist->mode;
449                 if (fb_mode_is_equal(m, mode)) {
450                         found = 1;
451                         break;
452                 }
453         }
454         if (!found) {
455                 modelist = kmalloc(sizeof(*modelist),
456                                    GFP_KERNEL);
457
458                 if (!modelist)
459                         return -ENOMEM;
460                 modelist->mode = *mode;
461                 list_add(&modelist->list, head);
462         }
463         return 0;
464 }
465
466 void rk_display_device_enable(struct rk_display_device *ddev)
467 {
468         struct list_head *pos, *head;
469         struct rk_display_device *dev = NULL, *dev_enabled = NULL;
470         struct rk_display_device *dev_enable = NULL;
471         int enable = 0, connect;
472
473         if (ddev->property == DISPLAY_MAIN)
474                 head = &main_display_device_list;
475         else
476                 head = &aux_display_device_list;
477
478         list_for_each(pos, head) {
479                 dev = list_entry(pos, struct rk_display_device, list);
480                 enable = dev->ops->getenable(dev);
481                 connect = dev->ops->getstatus(dev);
482                 if (connect)
483                         dev_enable = dev;
484                 if (enable == 1)
485                         dev_enabled = dev;
486         }
487         /* If no device is connected, enable highest priority device. */
488         if (dev_enable == NULL) {
489                 dev->ops->setenable(dev, 1);
490                 return;
491         }
492
493         if (dev_enable == dev_enabled) {
494                 if (dev_enable != ddev)
495                         ddev->ops->setenable(ddev, 0);
496         } else {
497                 if (dev_enabled &&
498                     dev_enabled->priority != DISPLAY_PRIORITY_HDMI)
499                         dev_enabled->ops->setenable(dev_enabled, 0);
500                 dev_enable->ops->setenable(dev_enable, 1);
501         }
502 }
503 EXPORT_SYMBOL(rk_display_device_enable);
504
505 void rk_display_device_enable_other(struct rk_display_device *ddev)
506 {
507 #ifndef CONFIG_DISPLAY_AUTO_SWITCH
508         return;
509 #else
510         struct list_head *pos, *head;
511         struct rk_display_device *dev;
512         int connect = 0;
513
514         if (ddev->property == DISPLAY_MAIN)
515                 head = &main_display_device_list;
516         else
517                 head = &aux_display_device_list;
518
519         list_for_each_prev(pos, head) {
520                 dev = list_entry(pos, struct rk_display_device, list);
521                 if (dev != ddev) {
522                         connect = dev->ops->getstatus(dev);
523                         if (connect) {
524                                 dev->ops->setenable(dev, 1);
525                                 return;
526                         }
527                 }
528         }
529 #endif
530 }
531 EXPORT_SYMBOL(rk_display_device_enable_other);
532
533 void rk_display_device_disable_other(struct rk_display_device *ddev)
534 {
535 #ifndef CONFIG_DISPLAY_AUTO_SWITCH
536         return;
537 #else
538         struct list_head *pos, *head;
539         struct rk_display_device *dev;
540         int enable = 0;
541
542         if (ddev->property == DISPLAY_MAIN)
543                 head = &main_display_device_list;
544         else
545                 head = &aux_display_device_list;
546
547         list_for_each(pos, head) {
548                 dev = list_entry(pos, struct rk_display_device, list);
549                 if (dev != ddev) {
550                         enable = dev->ops->getenable(dev);
551                         if (enable)
552                                 dev->ops->setenable(dev, 0);
553                 }
554         }
555         ddev->ops->setenable(ddev, 1);
556 #endif
557 }
558 EXPORT_SYMBOL(rk_display_device_disable_other);
559
560 void rk_display_device_select(int property, int priority)
561 {
562         struct list_head *pos, *head;
563         struct rk_display_device *dev;
564         int enable, found = 0;
565
566         if (property == DISPLAY_MAIN)
567                 head = &main_display_device_list;
568         else
569                 head = &aux_display_device_list;
570
571         list_for_each(pos, head) {
572                 dev = list_entry(pos, struct rk_display_device, list);
573                 if (dev->priority == priority)
574                         found = 1;
575         }
576
577         if (!found) {
578                 pr_err("[%s] select display interface %d not exist\n",
579                        __func__, priority);
580                 return;
581         }
582
583         list_for_each(pos, head) {
584                 dev = list_entry(pos, struct rk_display_device, list);
585                 enable = dev->ops->getenable(dev);
586                 if (dev->priority == priority) {
587                         if (!enable)
588                                 dev->ops->setenable(dev, 1);
589                 } else if (enable) {
590                         dev->ops->setenable(dev, 0);
591                 }
592         }
593 }
594 EXPORT_SYMBOL(rk_display_device_select);
595 static struct mutex allocated_dsp_lock;
596 static DEFINE_IDR(allocated_dsp);
597 static struct class *display_class;
598
599 struct rk_display_device
600         *rk_display_device_register(struct rk_display_driver *driver,
601                                     struct device *parent, void *devdata)
602 {
603         struct rk_display_device *new_dev = NULL;
604         int ret = -EINVAL;
605
606         if (unlikely(!driver))
607                 return ERR_PTR(ret);
608
609         new_dev = kzalloc(sizeof(*new_dev), GFP_KERNEL);
610         if (likely(new_dev) && unlikely(driver->probe(new_dev, devdata))) {
611                 /* Reserve the index for this display */
612                 mutex_lock(&allocated_dsp_lock);
613                 new_dev->idx = idr_alloc(&allocated_dsp, new_dev,
614                                          0, 0, GFP_KERNEL);
615                 mutex_unlock(&allocated_dsp_lock);
616
617                 if (new_dev->idx >= 0) {
618                         new_dev->dev =
619                         device_create(display_class, parent,
620                                       MKDEV(0, 0), new_dev,
621                                       "%s", new_dev->type);
622                         if (!IS_ERR(new_dev->dev)) {
623                                 new_dev->parent = parent;
624                                 new_dev->driver = driver;
625                                 if (parent)
626                                         new_dev->dev->driver = parent->driver;
627                                 mutex_init(&new_dev->lock);
628                                 /* Add new device to display device list. */
629                                 {
630                                 struct list_head *pos, *head;
631                                 struct rk_display_device *dev;
632
633                                 if (new_dev->property == DISPLAY_MAIN)
634                                         head = &main_display_device_list;
635                                 else
636                                         head = &aux_display_device_list;
637
638                                 list_for_each(pos, head) {
639                                         dev =
640                                         list_entry(pos,
641                                                    struct rk_display_device,
642                                                    list);
643                                         if (dev->priority > new_dev->priority)
644                                                 break;
645                                 }
646                                 list_add_tail(&new_dev->list, pos);
647                                 return new_dev;
648                                 }
649                         }
650                         mutex_lock(&allocated_dsp_lock);
651                         idr_remove(&allocated_dsp, new_dev->idx);
652                         mutex_unlock(&allocated_dsp_lock);
653                         ret = -EINVAL;
654                 }
655         }
656         kfree(new_dev);
657         return ERR_PTR(ret);
658 }
659 EXPORT_SYMBOL(rk_display_device_register);
660
661 void rk_display_device_unregister(struct rk_display_device *ddev)
662 {
663         if (!ddev)
664                 return;
665         /* Free device */
666         mutex_lock(&ddev->lock);
667         device_unregister(ddev->dev);
668         mutex_unlock(&ddev->lock);
669         /* Mark device index as avaliable */
670         mutex_lock(&allocated_dsp_lock);
671         idr_remove(&allocated_dsp, ddev->idx);
672         mutex_unlock(&allocated_dsp_lock);
673         list_del(&ddev->list);
674         kfree(ddev);
675 }
676 EXPORT_SYMBOL(rk_display_device_unregister);
677
678 static int __init rk_display_class_init(void)
679 {
680         display_class = class_create(THIS_MODULE, "display");
681         if (IS_ERR(display_class)) {
682                 pr_err("Failed to create display class\n");
683                 display_class = NULL;
684                 return -EINVAL;
685         }
686         display_class->dev_attrs = display_attrs;
687         display_class->suspend = display_suspend;
688         display_class->resume = display_resume;
689         mutex_init(&allocated_dsp_lock);
690         INIT_LIST_HEAD(&main_display_device_list);
691         INIT_LIST_HEAD(&aux_display_device_list);
692         return 0;
693 }
694
695 static void __exit rk_display_class_exit(void)
696 {
697         class_destroy(display_class);
698 }
699
700 subsys_initcall(rk_display_class_init);
701 module_exit(rk_display_class_exit);
702
703
704 MODULE_AUTHOR("zhengyang@rock-chips.com");
705 MODULE_DESCRIPTION("Driver for rk display device");
706 MODULE_LICENSE("GPL");