xref: /linux/arch/arm/mach-pxa/am200epd.c (revision 0898782247ae533d1f4e47a06bc5d4870931b284)
192261343SJaya Kumar /*
292261343SJaya Kumar  * am200epd.c -- Platform device for AM200 EPD kit
392261343SJaya Kumar  *
492261343SJaya Kumar  * Copyright (C) 2008, Jaya Kumar
592261343SJaya Kumar  *
692261343SJaya Kumar  * This file is subject to the terms and conditions of the GNU General Public
792261343SJaya Kumar  * License. See the file COPYING in the main directory of this archive for
892261343SJaya Kumar  * more details.
992261343SJaya Kumar  *
1092261343SJaya Kumar  * Layout is based on skeletonfb.c by James Simmons and Geert Uytterhoeven.
1192261343SJaya Kumar  *
1292261343SJaya Kumar  * This work was made possible by help and equipment support from E-Ink
1392261343SJaya Kumar  * Corporation. http://support.eink.com/community
1492261343SJaya Kumar  *
1592261343SJaya Kumar  * This driver is written to be used with the Metronome display controller.
1692261343SJaya Kumar  * on the AM200 EPD prototype kit/development kit with an E-Ink 800x600
1792261343SJaya Kumar  * Vizplex EPD on a Gumstix board using the Lyre interface board.
1892261343SJaya Kumar  *
1992261343SJaya Kumar  */
2092261343SJaya Kumar 
2192261343SJaya Kumar #include <linux/module.h>
2292261343SJaya Kumar #include <linux/kernel.h>
2392261343SJaya Kumar #include <linux/errno.h>
2492261343SJaya Kumar #include <linux/string.h>
2592261343SJaya Kumar #include <linux/delay.h>
2692261343SJaya Kumar #include <linux/interrupt.h>
2792261343SJaya Kumar #include <linux/fb.h>
2892261343SJaya Kumar #include <linux/init.h>
2992261343SJaya Kumar #include <linux/platform_device.h>
3092261343SJaya Kumar #include <linux/irq.h>
3192261343SJaya Kumar #include <linux/gpio.h>
3292261343SJaya Kumar 
334c25c5d2SArnd Bergmann #include "pxa25x.h"
344c25c5d2SArnd Bergmann #include "gumstix.h"
35293b2da1SArnd Bergmann #include <linux/platform_data/video-pxafb.h>
3692261343SJaya Kumar 
37854feaedSJaya Kumar #include "generic.h"
38854feaedSJaya Kumar 
3992261343SJaya Kumar #include <video/metronomefb.h>
4092261343SJaya Kumar 
4192261343SJaya Kumar static unsigned int panel_type = 6;
4292261343SJaya Kumar static struct platform_device *am200_device;
4392261343SJaya Kumar static struct metronome_board am200_board;
4492261343SJaya Kumar 
4592261343SJaya Kumar static struct pxafb_mode_info am200_fb_mode_9inch7 = {
4692261343SJaya Kumar 	.pixclock	= 40000,
4792261343SJaya Kumar 	.xres		= 1200,
4892261343SJaya Kumar 	.yres		= 842,
4992261343SJaya Kumar 	.bpp		= 16,
5092261343SJaya Kumar 	.hsync_len	= 2,
5192261343SJaya Kumar 	.left_margin	= 2,
5292261343SJaya Kumar 	.right_margin	= 2,
5392261343SJaya Kumar 	.vsync_len	= 1,
5492261343SJaya Kumar 	.upper_margin	= 2,
5592261343SJaya Kumar 	.lower_margin	= 25,
5692261343SJaya Kumar 	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
5792261343SJaya Kumar };
5892261343SJaya Kumar 
5992261343SJaya Kumar static struct pxafb_mode_info am200_fb_mode_8inch = {
6092261343SJaya Kumar 	.pixclock	= 40000,
6192261343SJaya Kumar 	.xres		= 1088,
6292261343SJaya Kumar 	.yres		= 791,
6392261343SJaya Kumar 	.bpp		= 16,
6492261343SJaya Kumar 	.hsync_len	= 28,
6592261343SJaya Kumar 	.left_margin	= 8,
6692261343SJaya Kumar 	.right_margin	= 30,
6792261343SJaya Kumar 	.vsync_len	= 8,
6892261343SJaya Kumar 	.upper_margin	= 10,
6992261343SJaya Kumar 	.lower_margin	= 8,
7092261343SJaya Kumar 	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
7192261343SJaya Kumar };
7292261343SJaya Kumar 
7392261343SJaya Kumar static struct pxafb_mode_info am200_fb_mode_6inch = {
7492261343SJaya Kumar 	.pixclock	= 40189,
7592261343SJaya Kumar 	.xres		= 832,
7692261343SJaya Kumar 	.yres		= 622,
7792261343SJaya Kumar 	.bpp		= 16,
7892261343SJaya Kumar 	.hsync_len	= 28,
7992261343SJaya Kumar 	.left_margin	= 34,
8092261343SJaya Kumar 	.right_margin	= 34,
8192261343SJaya Kumar 	.vsync_len	= 25,
8292261343SJaya Kumar 	.upper_margin	= 0,
8392261343SJaya Kumar 	.lower_margin	= 2,
8492261343SJaya Kumar 	.sync		= FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT,
8592261343SJaya Kumar };
8692261343SJaya Kumar 
8792261343SJaya Kumar static struct pxafb_mach_info am200_fb_info = {
8892261343SJaya Kumar 	.modes		= &am200_fb_mode_6inch,
8992261343SJaya Kumar 	.num_modes	= 1,
9092261343SJaya Kumar 	.lcd_conn	= LCD_TYPE_COLOR_TFT | LCD_PCLK_EDGE_FALL |
9192261343SJaya Kumar 			  LCD_AC_BIAS_FREQ(24),
9292261343SJaya Kumar };
9392261343SJaya Kumar 
9492261343SJaya Kumar /* register offsets for gpio control */
9592261343SJaya Kumar #define LED_GPIO_PIN 51
9692261343SJaya Kumar #define STDBY_GPIO_PIN 48
9792261343SJaya Kumar #define RST_GPIO_PIN 49
9892261343SJaya Kumar #define RDY_GPIO_PIN 32
9992261343SJaya Kumar #define ERR_GPIO_PIN 17
10092261343SJaya Kumar #define PCBPWR_GPIO_PIN 16
10192261343SJaya Kumar static int gpios[] = { LED_GPIO_PIN , STDBY_GPIO_PIN , RST_GPIO_PIN,
10292261343SJaya Kumar 			RDY_GPIO_PIN, ERR_GPIO_PIN, PCBPWR_GPIO_PIN };
10392261343SJaya Kumar static char *gpio_names[] = { "LED" , "STDBY" , "RST", "RDY", "ERR", "PCBPWR" };
10492261343SJaya Kumar 
10592261343SJaya Kumar static int am200_init_gpio_regs(struct metronomefb_par *par)
10692261343SJaya Kumar {
10792261343SJaya Kumar 	int i;
10892261343SJaya Kumar 	int err;
10992261343SJaya Kumar 
11092261343SJaya Kumar 	for (i = 0; i < ARRAY_SIZE(gpios); i++) {
11192261343SJaya Kumar 		err = gpio_request(gpios[i], gpio_names[i]);
11292261343SJaya Kumar 		if (err) {
11392261343SJaya Kumar 			dev_err(&am200_device->dev, "failed requesting "
11492261343SJaya Kumar 				"gpio %s, err=%d\n", gpio_names[i], err);
11592261343SJaya Kumar 			goto err_req_gpio;
11692261343SJaya Kumar 		}
11792261343SJaya Kumar 	}
11892261343SJaya Kumar 
11992261343SJaya Kumar 	gpio_direction_output(LED_GPIO_PIN, 0);
12092261343SJaya Kumar 	gpio_direction_output(STDBY_GPIO_PIN, 0);
12192261343SJaya Kumar 	gpio_direction_output(RST_GPIO_PIN, 0);
12292261343SJaya Kumar 
12392261343SJaya Kumar 	gpio_direction_input(RDY_GPIO_PIN);
12492261343SJaya Kumar 	gpio_direction_input(ERR_GPIO_PIN);
12592261343SJaya Kumar 
12692261343SJaya Kumar 	gpio_direction_output(PCBPWR_GPIO_PIN, 0);
12792261343SJaya Kumar 
12892261343SJaya Kumar 	return 0;
12992261343SJaya Kumar 
13092261343SJaya Kumar err_req_gpio:
1318aad172eSAxel Lin 	while (--i >= 0)
1328aad172eSAxel Lin 		gpio_free(gpios[i]);
13392261343SJaya Kumar 
13492261343SJaya Kumar 	return err;
13592261343SJaya Kumar }
13692261343SJaya Kumar 
13792261343SJaya Kumar static void am200_cleanup(struct metronomefb_par *par)
13892261343SJaya Kumar {
13992261343SJaya Kumar 	int i;
14092261343SJaya Kumar 
1416384fdadSHaojian Zhuang 	free_irq(PXA_GPIO_TO_IRQ(RDY_GPIO_PIN), par);
14292261343SJaya Kumar 
14392261343SJaya Kumar 	for (i = 0; i < ARRAY_SIZE(gpios); i++)
14492261343SJaya Kumar 		gpio_free(gpios[i]);
14592261343SJaya Kumar }
14692261343SJaya Kumar 
14792261343SJaya Kumar static int am200_share_video_mem(struct fb_info *info)
14892261343SJaya Kumar {
14992261343SJaya Kumar 	/* rough check if this is our desired fb and not something else */
15092261343SJaya Kumar 	if ((info->var.xres != am200_fb_info.modes->xres)
15192261343SJaya Kumar 		|| (info->var.yres != am200_fb_info.modes->yres))
15292261343SJaya Kumar 		return 0;
15392261343SJaya Kumar 
15492261343SJaya Kumar 	/* we've now been notified that we have our new fb */
15592261343SJaya Kumar 	am200_board.metromem = info->screen_base;
15692261343SJaya Kumar 	am200_board.host_fbinfo = info;
15792261343SJaya Kumar 
15892261343SJaya Kumar 	/* try to refcount host drv since we are the consumer after this */
15992261343SJaya Kumar 	if (!try_module_get(info->fbops->owner))
16092261343SJaya Kumar 		return -ENODEV;
16192261343SJaya Kumar 
16292261343SJaya Kumar 	return 0;
16392261343SJaya Kumar }
16492261343SJaya Kumar 
16592261343SJaya Kumar static int am200_unshare_video_mem(struct fb_info *info)
16692261343SJaya Kumar {
16792261343SJaya Kumar 	dev_dbg(&am200_device->dev, "ENTER %s\n", __func__);
16892261343SJaya Kumar 
16992261343SJaya Kumar 	if (info != am200_board.host_fbinfo)
17092261343SJaya Kumar 		return 0;
17192261343SJaya Kumar 
17292261343SJaya Kumar 	module_put(am200_board.host_fbinfo->fbops->owner);
17392261343SJaya Kumar 	return 0;
17492261343SJaya Kumar }
17592261343SJaya Kumar 
17692261343SJaya Kumar static int am200_fb_notifier_callback(struct notifier_block *self,
17792261343SJaya Kumar 				 unsigned long event, void *data)
17892261343SJaya Kumar {
17992261343SJaya Kumar 	struct fb_event *evdata = data;
18092261343SJaya Kumar 	struct fb_info *info = evdata->info;
18192261343SJaya Kumar 
18292261343SJaya Kumar 	dev_dbg(&am200_device->dev, "ENTER %s\n", __func__);
18392261343SJaya Kumar 
18492261343SJaya Kumar 	if (event == FB_EVENT_FB_REGISTERED)
18592261343SJaya Kumar 		return am200_share_video_mem(info);
18692261343SJaya Kumar 	else if (event == FB_EVENT_FB_UNREGISTERED)
18792261343SJaya Kumar 		return am200_unshare_video_mem(info);
18892261343SJaya Kumar 
18992261343SJaya Kumar 	return 0;
19092261343SJaya Kumar }
19192261343SJaya Kumar 
19292261343SJaya Kumar static struct notifier_block am200_fb_notif = {
19392261343SJaya Kumar 	.notifier_call = am200_fb_notifier_callback,
19492261343SJaya Kumar };
19592261343SJaya Kumar 
19692261343SJaya Kumar /* this gets called as part of our init. these steps must be done now so
1974321e1a1SRussell King - ARM Linux  * that we can use pxa_set_fb_info */
19892261343SJaya Kumar static void __init am200_presetup_fb(void)
19992261343SJaya Kumar {
20092261343SJaya Kumar 	int fw;
20192261343SJaya Kumar 	int fh;
20292261343SJaya Kumar 	int padding_size;
20392261343SJaya Kumar 	int totalsize;
20492261343SJaya Kumar 
20592261343SJaya Kumar 	switch (panel_type) {
20692261343SJaya Kumar 	case 6:
20792261343SJaya Kumar 		am200_fb_info.modes = &am200_fb_mode_6inch;
20892261343SJaya Kumar 		break;
20992261343SJaya Kumar 	case 8:
21092261343SJaya Kumar 		am200_fb_info.modes = &am200_fb_mode_8inch;
21192261343SJaya Kumar 		break;
21292261343SJaya Kumar 	case 97:
21392261343SJaya Kumar 		am200_fb_info.modes = &am200_fb_mode_9inch7;
21492261343SJaya Kumar 		break;
21592261343SJaya Kumar 	default:
21692261343SJaya Kumar 		dev_err(&am200_device->dev, "invalid panel_type selection,"
21792261343SJaya Kumar 						" setting to 6\n");
21892261343SJaya Kumar 		am200_fb_info.modes = &am200_fb_mode_6inch;
21992261343SJaya Kumar 		break;
22092261343SJaya Kumar 	}
22192261343SJaya Kumar 
22292261343SJaya Kumar 	/* the frame buffer is divided as follows:
22392261343SJaya Kumar 	command | CRC | padding
22492261343SJaya Kumar 	16kb waveform data | CRC | padding
22592261343SJaya Kumar 	image data | CRC
22692261343SJaya Kumar 	*/
22792261343SJaya Kumar 
22892261343SJaya Kumar 	fw = am200_fb_info.modes->xres;
22992261343SJaya Kumar 	fh = am200_fb_info.modes->yres;
23092261343SJaya Kumar 
23192261343SJaya Kumar 	/* waveform must be 16k + 2 for checksum */
23292261343SJaya Kumar 	am200_board.wfm_size = roundup(16*1024 + 2, fw);
23392261343SJaya Kumar 
23492261343SJaya Kumar 	padding_size = PAGE_SIZE + (4 * fw);
23592261343SJaya Kumar 
23692261343SJaya Kumar 	/* total is 1 cmd , 1 wfm, padding and image */
23792261343SJaya Kumar 	totalsize = fw + am200_board.wfm_size + padding_size + (fw*fh);
23892261343SJaya Kumar 
23992261343SJaya Kumar 	/* save this off because we're manipulating fw after this and
24092261343SJaya Kumar 	 * we'll need it when we're ready to setup the framebuffer */
24192261343SJaya Kumar 	am200_board.fw = fw;
24292261343SJaya Kumar 	am200_board.fh = fh;
24392261343SJaya Kumar 
24492261343SJaya Kumar 	/* the reason we do this adjustment is because we want to acquire
24592261343SJaya Kumar 	 * more framebuffer memory without imposing custom awareness on the
24692261343SJaya Kumar 	 * underlying pxafb driver */
24792261343SJaya Kumar 	am200_fb_info.modes->yres = DIV_ROUND_UP(totalsize, fw);
24892261343SJaya Kumar 
24992261343SJaya Kumar 	/* we divide since we told the LCD controller we're 16bpp */
25092261343SJaya Kumar 	am200_fb_info.modes->xres /= 2;
25192261343SJaya Kumar 
2524321e1a1SRussell King - ARM Linux 	pxa_set_fb_info(NULL, &am200_fb_info);
25392261343SJaya Kumar 
25492261343SJaya Kumar }
25592261343SJaya Kumar 
25692261343SJaya Kumar /* this gets called by metronomefb as part of its init, in our case, we
25792261343SJaya Kumar  * have already completed initial framebuffer init in presetup_fb so we
25892261343SJaya Kumar  * can just setup the fb access pointers */
25992261343SJaya Kumar static int am200_setup_fb(struct metronomefb_par *par)
26092261343SJaya Kumar {
26192261343SJaya Kumar 	int fw;
26292261343SJaya Kumar 	int fh;
26392261343SJaya Kumar 
26492261343SJaya Kumar 	fw = am200_board.fw;
26592261343SJaya Kumar 	fh = am200_board.fh;
26692261343SJaya Kumar 
26792261343SJaya Kumar 	/* metromem was set up by the notifier in share_video_mem so now
26892261343SJaya Kumar 	 * we can use its value to calculate the other entries */
26992261343SJaya Kumar 	par->metromem_cmd = (struct metromem_cmd *) am200_board.metromem;
27092261343SJaya Kumar 	par->metromem_wfm = am200_board.metromem + fw;
27192261343SJaya Kumar 	par->metromem_img = par->metromem_wfm + am200_board.wfm_size;
27292261343SJaya Kumar 	par->metromem_img_csum = (u16 *) (par->metromem_img + (fw * fh));
27392261343SJaya Kumar 	par->metromem_dma = am200_board.host_fbinfo->fix.smem_start;
27492261343SJaya Kumar 
27592261343SJaya Kumar 	return 0;
27692261343SJaya Kumar }
27792261343SJaya Kumar 
27892261343SJaya Kumar static int am200_get_panel_type(void)
27992261343SJaya Kumar {
28092261343SJaya Kumar 	return panel_type;
28192261343SJaya Kumar }
28292261343SJaya Kumar 
28392261343SJaya Kumar static irqreturn_t am200_handle_irq(int irq, void *dev_id)
28492261343SJaya Kumar {
28592261343SJaya Kumar 	struct metronomefb_par *par = dev_id;
28692261343SJaya Kumar 
28792261343SJaya Kumar 	wake_up_interruptible(&par->waitq);
28892261343SJaya Kumar 	return IRQ_HANDLED;
28992261343SJaya Kumar }
29092261343SJaya Kumar 
29192261343SJaya Kumar static int am200_setup_irq(struct fb_info *info)
29292261343SJaya Kumar {
29392261343SJaya Kumar 	int ret;
29492261343SJaya Kumar 
2956384fdadSHaojian Zhuang 	ret = request_irq(PXA_GPIO_TO_IRQ(RDY_GPIO_PIN), am200_handle_irq,
296ed7936f9SMichael Opdenacker 				IRQF_TRIGGER_FALLING, "AM200", info->par);
29792261343SJaya Kumar 	if (ret)
29892261343SJaya Kumar 		dev_err(&am200_device->dev, "request_irq failed: %d\n", ret);
29992261343SJaya Kumar 
30092261343SJaya Kumar 	return ret;
30192261343SJaya Kumar }
30292261343SJaya Kumar 
30392261343SJaya Kumar static void am200_set_rst(struct metronomefb_par *par, int state)
30492261343SJaya Kumar {
30592261343SJaya Kumar 	gpio_set_value(RST_GPIO_PIN, state);
30692261343SJaya Kumar }
30792261343SJaya Kumar 
30892261343SJaya Kumar static void am200_set_stdby(struct metronomefb_par *par, int state)
30992261343SJaya Kumar {
31092261343SJaya Kumar 	gpio_set_value(STDBY_GPIO_PIN, state);
31192261343SJaya Kumar }
31292261343SJaya Kumar 
31392261343SJaya Kumar static int am200_wait_event(struct metronomefb_par *par)
31492261343SJaya Kumar {
31592261343SJaya Kumar 	return wait_event_timeout(par->waitq, gpio_get_value(RDY_GPIO_PIN), HZ);
31692261343SJaya Kumar }
31792261343SJaya Kumar 
31892261343SJaya Kumar static int am200_wait_event_intr(struct metronomefb_par *par)
31992261343SJaya Kumar {
32092261343SJaya Kumar 	return wait_event_interruptible_timeout(par->waitq,
32192261343SJaya Kumar 					gpio_get_value(RDY_GPIO_PIN), HZ);
32292261343SJaya Kumar }
32392261343SJaya Kumar 
32492261343SJaya Kumar static struct metronome_board am200_board = {
32592261343SJaya Kumar 	.owner			= THIS_MODULE,
32692261343SJaya Kumar 	.setup_irq		= am200_setup_irq,
32792261343SJaya Kumar 	.setup_io		= am200_init_gpio_regs,
32892261343SJaya Kumar 	.setup_fb		= am200_setup_fb,
32992261343SJaya Kumar 	.set_rst		= am200_set_rst,
33092261343SJaya Kumar 	.set_stdby		= am200_set_stdby,
33192261343SJaya Kumar 	.met_wait_event		= am200_wait_event,
33292261343SJaya Kumar 	.met_wait_event_intr	= am200_wait_event_intr,
33392261343SJaya Kumar 	.get_panel_type		= am200_get_panel_type,
33492261343SJaya Kumar 	.cleanup		= am200_cleanup,
33592261343SJaya Kumar };
33692261343SJaya Kumar 
337854feaedSJaya Kumar static unsigned long am200_pin_config[] __initdata = {
338854feaedSJaya Kumar 	GPIO51_GPIO,
339854feaedSJaya Kumar 	GPIO49_GPIO,
340854feaedSJaya Kumar 	GPIO48_GPIO,
341854feaedSJaya Kumar 	GPIO32_GPIO,
342854feaedSJaya Kumar 	GPIO17_GPIO,
343854feaedSJaya Kumar 	GPIO16_GPIO,
344854feaedSJaya Kumar };
345854feaedSJaya Kumar 
3463332b0c1SJaya Kumar int __init am200_init(void)
34792261343SJaya Kumar {
34892261343SJaya Kumar 	int ret;
34992261343SJaya Kumar 
350*97b67986SDaniel Vetter 	/*
351*97b67986SDaniel Vetter 	 * Before anything else, we request notification for any fb
352*97b67986SDaniel Vetter 	 * creation events.
353*97b67986SDaniel Vetter 	 *
354*97b67986SDaniel Vetter 	 * FIXME: This is terrible and needs to be nuked. The notifier is used
355*97b67986SDaniel Vetter 	 * to get at the fb base address from the boot splash fb driver, which
356*97b67986SDaniel Vetter 	 * is then passed to metronomefb. Instaed of metronomfb or this board
357*97b67986SDaniel Vetter 	 * support file here figuring this out on their own.
358*97b67986SDaniel Vetter 	 *
359*97b67986SDaniel Vetter 	 * See also the #ifdef in fbmem.c.
360*97b67986SDaniel Vetter 	 */
36192261343SJaya Kumar 	fb_register_client(&am200_fb_notif);
36292261343SJaya Kumar 
363854feaedSJaya Kumar 	pxa2xx_mfp_config(ARRAY_AND_SIZE(am200_pin_config));
364854feaedSJaya Kumar 
36592261343SJaya Kumar 	/* request our platform independent driver */
36692261343SJaya Kumar 	request_module("metronomefb");
36792261343SJaya Kumar 
36892261343SJaya Kumar 	am200_device = platform_device_alloc("metronomefb", -1);
36992261343SJaya Kumar 	if (!am200_device)
37092261343SJaya Kumar 		return -ENOMEM;
37192261343SJaya Kumar 
37292261343SJaya Kumar 	/* the am200_board that will be seen by metronomefb is a copy */
37392261343SJaya Kumar 	platform_device_add_data(am200_device, &am200_board,
37492261343SJaya Kumar 					sizeof(am200_board));
37592261343SJaya Kumar 
37692261343SJaya Kumar 	/* this _add binds metronomefb to am200. metronomefb refcounts am200 */
37792261343SJaya Kumar 	ret = platform_device_add(am200_device);
37892261343SJaya Kumar 
37992261343SJaya Kumar 	if (ret) {
38092261343SJaya Kumar 		platform_device_put(am200_device);
38192261343SJaya Kumar 		fb_unregister_client(&am200_fb_notif);
38292261343SJaya Kumar 		return ret;
38392261343SJaya Kumar 	}
38492261343SJaya Kumar 
38592261343SJaya Kumar 	am200_presetup_fb();
38692261343SJaya Kumar 
38792261343SJaya Kumar 	return 0;
38892261343SJaya Kumar }
38992261343SJaya Kumar 
39092261343SJaya Kumar module_param(panel_type, uint, 0);
39192261343SJaya Kumar MODULE_PARM_DESC(panel_type, "Select the panel type: 6, 8, 97");
39292261343SJaya Kumar 
39392261343SJaya Kumar MODULE_DESCRIPTION("board driver for am200 metronome epd kit");
39492261343SJaya Kumar MODULE_AUTHOR("Jaya Kumar");
39592261343SJaya Kumar MODULE_LICENSE("GPL");
396