xref: /openbmc/linux/sound/soc/img/img-i2s-out.c (revision 12653414)
1 /*
2  * IMG I2S output controller driver
3  *
4  * Copyright (C) 2015 Imagination Technologies Ltd.
5  *
6  * Author: Damien Horsley <Damien.Horsley@imgtec.com>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms and conditions of the GNU General Public License,
10  * version 2, as published by the Free Software Foundation.
11  */
12 
13 #include <linux/clk.h>
14 #include <linux/init.h>
15 #include <linux/kernel.h>
16 #include <linux/module.h>
17 #include <linux/of.h>
18 #include <linux/platform_device.h>
19 #include <linux/pm_runtime.h>
20 #include <linux/reset.h>
21 
22 #include <sound/core.h>
23 #include <sound/dmaengine_pcm.h>
24 #include <sound/initval.h>
25 #include <sound/pcm.h>
26 #include <sound/pcm_params.h>
27 #include <sound/soc.h>
28 
29 #define IMG_I2S_OUT_TX_FIFO			0x0
30 
31 #define IMG_I2S_OUT_CTL				0x4
32 #define IMG_I2S_OUT_CTL_DATA_EN_MASK		BIT(24)
33 #define IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK	0xffe000
34 #define IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT	13
35 #define IMG_I2S_OUT_CTL_FRM_SIZE_MASK		BIT(8)
36 #define IMG_I2S_OUT_CTL_MASTER_MASK		BIT(6)
37 #define IMG_I2S_OUT_CTL_CLK_MASK		BIT(5)
38 #define IMG_I2S_OUT_CTL_CLK_EN_MASK		BIT(4)
39 #define IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK	BIT(3)
40 #define IMG_I2S_OUT_CTL_BCLK_POL_MASK		BIT(2)
41 #define IMG_I2S_OUT_CTL_ME_MASK			BIT(0)
42 
43 #define IMG_I2S_OUT_CH_CTL			0x4
44 #define IMG_I2S_OUT_CHAN_CTL_CH_MASK		BIT(11)
45 #define IMG_I2S_OUT_CHAN_CTL_LT_MASK		BIT(10)
46 #define IMG_I2S_OUT_CHAN_CTL_FMT_MASK		0xf0
47 #define IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT		4
48 #define IMG_I2S_OUT_CHAN_CTL_JUST_MASK		BIT(3)
49 #define IMG_I2S_OUT_CHAN_CTL_CLKT_MASK		BIT(1)
50 #define IMG_I2S_OUT_CHAN_CTL_ME_MASK		BIT(0)
51 
52 #define IMG_I2S_OUT_CH_STRIDE			0x20
53 
54 struct img_i2s_out {
55 	void __iomem *base;
56 	struct clk *clk_sys;
57 	struct clk *clk_ref;
58 	struct snd_dmaengine_dai_dma_data dma_data;
59 	struct device *dev;
60 	unsigned int max_i2s_chan;
61 	void __iomem *channel_base;
62 	bool force_clk_active;
63 	unsigned int active_channels;
64 	struct reset_control *rst;
65 	struct snd_soc_dai_driver dai_driver;
66 };
67 
68 static int img_i2s_out_suspend(struct device *dev)
69 {
70 	struct img_i2s_out *i2s = dev_get_drvdata(dev);
71 
72 	if (!i2s->force_clk_active)
73 		clk_disable_unprepare(i2s->clk_ref);
74 
75 	return 0;
76 }
77 
78 static int img_i2s_out_resume(struct device *dev)
79 {
80 	struct img_i2s_out *i2s = dev_get_drvdata(dev);
81 	int ret;
82 
83 	if (!i2s->force_clk_active) {
84 		ret = clk_prepare_enable(i2s->clk_ref);
85 		if (ret) {
86 			dev_err(dev, "clk_enable failed: %d\n", ret);
87 			return ret;
88 		}
89 	}
90 
91 	return 0;
92 }
93 
94 static inline void img_i2s_out_writel(struct img_i2s_out *i2s, u32 val,
95 					u32 reg)
96 {
97 	writel(val, i2s->base + reg);
98 }
99 
100 static inline u32 img_i2s_out_readl(struct img_i2s_out *i2s, u32 reg)
101 {
102 	return readl(i2s->base + reg);
103 }
104 
105 static inline void img_i2s_out_ch_writel(struct img_i2s_out *i2s,
106 					u32 chan, u32 val, u32 reg)
107 {
108 	writel(val, i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg);
109 }
110 
111 static inline u32 img_i2s_out_ch_readl(struct img_i2s_out *i2s, u32 chan,
112 					u32 reg)
113 {
114 	return readl(i2s->channel_base + (chan * IMG_I2S_OUT_CH_STRIDE) + reg);
115 }
116 
117 static inline void img_i2s_out_ch_disable(struct img_i2s_out *i2s, u32 chan)
118 {
119 	u32 reg;
120 
121 	reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL);
122 	reg &= ~IMG_I2S_OUT_CHAN_CTL_ME_MASK;
123 	img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL);
124 }
125 
126 static inline void img_i2s_out_ch_enable(struct img_i2s_out *i2s, u32 chan)
127 {
128 	u32 reg;
129 
130 	reg = img_i2s_out_ch_readl(i2s, chan, IMG_I2S_OUT_CH_CTL);
131 	reg |= IMG_I2S_OUT_CHAN_CTL_ME_MASK;
132 	img_i2s_out_ch_writel(i2s, chan, reg, IMG_I2S_OUT_CH_CTL);
133 }
134 
135 static inline void img_i2s_out_disable(struct img_i2s_out *i2s)
136 {
137 	u32 reg;
138 
139 	reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
140 	reg &= ~IMG_I2S_OUT_CTL_ME_MASK;
141 	img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
142 }
143 
144 static inline void img_i2s_out_enable(struct img_i2s_out *i2s)
145 {
146 	u32 reg;
147 
148 	reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
149 	reg |= IMG_I2S_OUT_CTL_ME_MASK;
150 	img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
151 }
152 
153 static void img_i2s_out_reset(struct img_i2s_out *i2s)
154 {
155 	int i;
156 	u32 core_ctl, chan_ctl;
157 
158 	core_ctl = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL) &
159 			~IMG_I2S_OUT_CTL_ME_MASK &
160 			~IMG_I2S_OUT_CTL_DATA_EN_MASK;
161 
162 	if (!i2s->force_clk_active)
163 		core_ctl &= ~IMG_I2S_OUT_CTL_CLK_EN_MASK;
164 
165 	chan_ctl = img_i2s_out_ch_readl(i2s, 0, IMG_I2S_OUT_CH_CTL) &
166 			~IMG_I2S_OUT_CHAN_CTL_ME_MASK;
167 
168 	reset_control_assert(i2s->rst);
169 	reset_control_deassert(i2s->rst);
170 
171 	for (i = 0; i < i2s->max_i2s_chan; i++)
172 		img_i2s_out_ch_writel(i2s, i, chan_ctl, IMG_I2S_OUT_CH_CTL);
173 
174 	for (i = 0; i < i2s->active_channels; i++)
175 		img_i2s_out_ch_enable(i2s, i);
176 
177 	img_i2s_out_writel(i2s, core_ctl, IMG_I2S_OUT_CTL);
178 	img_i2s_out_enable(i2s);
179 }
180 
181 static int img_i2s_out_trigger(struct snd_pcm_substream *substream, int cmd,
182 	struct snd_soc_dai *dai)
183 {
184 	struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
185 	u32 reg;
186 
187 	switch (cmd) {
188 	case SNDRV_PCM_TRIGGER_START:
189 	case SNDRV_PCM_TRIGGER_RESUME:
190 	case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
191 		reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
192 		if (!i2s->force_clk_active)
193 			reg |= IMG_I2S_OUT_CTL_CLK_EN_MASK;
194 		reg |= IMG_I2S_OUT_CTL_DATA_EN_MASK;
195 		img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
196 		break;
197 	case SNDRV_PCM_TRIGGER_STOP:
198 	case SNDRV_PCM_TRIGGER_SUSPEND:
199 	case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
200 		img_i2s_out_reset(i2s);
201 		break;
202 	default:
203 		return -EINVAL;
204 	}
205 
206 	return 0;
207 }
208 
209 static int img_i2s_out_hw_params(struct snd_pcm_substream *substream,
210 	struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
211 {
212 	struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
213 	unsigned int channels, i2s_channels;
214 	long pre_div_a, pre_div_b, diff_a, diff_b, rate, clk_rate;
215 	int i;
216 	u32 reg, control_mask, control_set = 0;
217 	snd_pcm_format_t format;
218 
219 	rate = params_rate(params);
220 	format = params_format(params);
221 	channels = params_channels(params);
222 	i2s_channels = channels / 2;
223 
224 	if (format != SNDRV_PCM_FORMAT_S32_LE)
225 		return -EINVAL;
226 
227 	if ((channels < 2) ||
228 	    (channels > (i2s->max_i2s_chan * 2)) ||
229 	    (channels % 2))
230 		return -EINVAL;
231 
232 	pre_div_a = clk_round_rate(i2s->clk_ref, rate * 256);
233 	if (pre_div_a < 0)
234 		return pre_div_a;
235 	pre_div_b = clk_round_rate(i2s->clk_ref, rate * 384);
236 	if (pre_div_b < 0)
237 		return pre_div_b;
238 
239 	diff_a = abs((pre_div_a / 256) - rate);
240 	diff_b = abs((pre_div_b / 384) - rate);
241 
242 	/* If diffs are equal, use lower clock rate */
243 	if (diff_a > diff_b)
244 		clk_set_rate(i2s->clk_ref, pre_div_b);
245 	else
246 		clk_set_rate(i2s->clk_ref, pre_div_a);
247 
248 	/*
249 	 * Another driver (eg alsa machine driver) may have rejected the above
250 	 * change. Get the current rate and set the register bit according to
251 	 * the new minimum diff
252 	 */
253 	clk_rate = clk_get_rate(i2s->clk_ref);
254 
255 	diff_a = abs((clk_rate / 256) - rate);
256 	diff_b = abs((clk_rate / 384) - rate);
257 
258 	if (diff_a > diff_b)
259 		control_set |= IMG_I2S_OUT_CTL_CLK_MASK;
260 
261 	control_set |= ((i2s_channels - 1) <<
262 		       IMG_I2S_OUT_CTL_ACTIVE_CHAN_SHIFT) &
263 		       IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK;
264 
265 	control_mask = IMG_I2S_OUT_CTL_CLK_MASK |
266 		       IMG_I2S_OUT_CTL_ACTIVE_CHAN_MASK;
267 
268 	img_i2s_out_disable(i2s);
269 
270 	reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
271 	reg = (reg & ~control_mask) | control_set;
272 	img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
273 
274 	for (i = 0; i < i2s_channels; i++)
275 		img_i2s_out_ch_enable(i2s, i);
276 
277 	for (; i < i2s->max_i2s_chan; i++)
278 		img_i2s_out_ch_disable(i2s, i);
279 
280 	img_i2s_out_enable(i2s);
281 
282 	i2s->active_channels = i2s_channels;
283 
284 	return 0;
285 }
286 
287 static int img_i2s_out_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
288 {
289 	struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
290 	int i;
291 	bool force_clk_active;
292 	u32 chan_control_mask, control_mask, chan_control_set = 0;
293 	u32 reg, control_set = 0;
294 
295 	force_clk_active = ((fmt & SND_SOC_DAIFMT_CLOCK_MASK) ==
296 			SND_SOC_DAIFMT_CONT);
297 
298 	if (force_clk_active)
299 		control_set |= IMG_I2S_OUT_CTL_CLK_EN_MASK;
300 
301 	switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
302 	case SND_SOC_DAIFMT_CBM_CFM:
303 		break;
304 	case SND_SOC_DAIFMT_CBS_CFS:
305 		control_set |= IMG_I2S_OUT_CTL_MASTER_MASK;
306 		break;
307 	default:
308 		return -EINVAL;
309 	}
310 
311 	switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
312 	case SND_SOC_DAIFMT_NB_NF:
313 		control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK;
314 		break;
315 	case SND_SOC_DAIFMT_NB_IF:
316 		control_set |= IMG_I2S_OUT_CTL_BCLK_POL_MASK;
317 		control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK;
318 		break;
319 	case SND_SOC_DAIFMT_IB_NF:
320 		break;
321 	case SND_SOC_DAIFMT_IB_IF:
322 		control_set |= IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK;
323 		break;
324 	default:
325 		return -EINVAL;
326 	}
327 
328 	switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
329 	case SND_SOC_DAIFMT_I2S:
330 		chan_control_set |= IMG_I2S_OUT_CHAN_CTL_CLKT_MASK;
331 		break;
332 	case SND_SOC_DAIFMT_LEFT_J:
333 		break;
334 	default:
335 		return -EINVAL;
336 	}
337 
338 	control_mask = IMG_I2S_OUT_CTL_CLK_EN_MASK |
339 		       IMG_I2S_OUT_CTL_MASTER_MASK |
340 		       IMG_I2S_OUT_CTL_BCLK_POL_MASK |
341 		       IMG_I2S_OUT_CTL_FRM_CLK_POL_MASK;
342 
343 	chan_control_mask = IMG_I2S_OUT_CHAN_CTL_CLKT_MASK;
344 
345 	img_i2s_out_disable(i2s);
346 
347 	reg = img_i2s_out_readl(i2s, IMG_I2S_OUT_CTL);
348 	reg = (reg & ~control_mask) | control_set;
349 	img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
350 
351 	for (i = 0; i < i2s->active_channels; i++)
352 		img_i2s_out_ch_disable(i2s, i);
353 
354 	for (i = 0; i < i2s->max_i2s_chan; i++) {
355 		reg = img_i2s_out_ch_readl(i2s, i, IMG_I2S_OUT_CH_CTL);
356 		reg = (reg & ~chan_control_mask) | chan_control_set;
357 		img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
358 	}
359 
360 	for (i = 0; i < i2s->active_channels; i++)
361 		img_i2s_out_ch_enable(i2s, i);
362 
363 	img_i2s_out_enable(i2s);
364 
365 	i2s->force_clk_active = force_clk_active;
366 
367 	return 0;
368 }
369 
370 static const struct snd_soc_dai_ops img_i2s_out_dai_ops = {
371 	.trigger = img_i2s_out_trigger,
372 	.hw_params = img_i2s_out_hw_params,
373 	.set_fmt = img_i2s_out_set_fmt
374 };
375 
376 static int img_i2s_out_dai_probe(struct snd_soc_dai *dai)
377 {
378 	struct img_i2s_out *i2s = snd_soc_dai_get_drvdata(dai);
379 
380 	snd_soc_dai_init_dma_data(dai, &i2s->dma_data, NULL);
381 
382 	return 0;
383 }
384 
385 static const struct snd_soc_component_driver img_i2s_out_component = {
386 	.name = "img-i2s-out"
387 };
388 
389 static int img_i2s_out_dma_prepare_slave_config(struct snd_pcm_substream *st,
390 	struct snd_pcm_hw_params *params, struct dma_slave_config *sc)
391 {
392 	unsigned int i2s_channels = params_channels(params) / 2;
393 	struct snd_soc_pcm_runtime *rtd = st->private_data;
394 	struct snd_dmaengine_dai_dma_data *dma_data;
395 	int ret;
396 
397 	dma_data = snd_soc_dai_get_dma_data(rtd->cpu_dai, st);
398 
399 	ret = snd_hwparams_to_dma_slave_config(st, params, sc);
400 	if (ret)
401 		return ret;
402 
403 	sc->dst_addr = dma_data->addr;
404 	sc->dst_addr_width = dma_data->addr_width;
405 	sc->dst_maxburst = 4 * i2s_channels;
406 
407 	return 0;
408 }
409 
410 static const struct snd_dmaengine_pcm_config img_i2s_out_dma_config = {
411 	.prepare_slave_config = img_i2s_out_dma_prepare_slave_config
412 };
413 
414 static int img_i2s_out_probe(struct platform_device *pdev)
415 {
416 	struct img_i2s_out *i2s;
417 	struct resource *res;
418 	void __iomem *base;
419 	int i, ret;
420 	unsigned int max_i2s_chan_pow_2;
421 	u32 reg;
422 	struct device *dev = &pdev->dev;
423 
424 	i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
425 	if (!i2s)
426 		return -ENOMEM;
427 
428 	platform_set_drvdata(pdev, i2s);
429 
430 	i2s->dev = &pdev->dev;
431 
432 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
433 	base = devm_ioremap_resource(&pdev->dev, res);
434 	if (IS_ERR(base))
435 		return PTR_ERR(base);
436 
437 	i2s->base = base;
438 
439 	if (of_property_read_u32(pdev->dev.of_node, "img,i2s-channels",
440 			&i2s->max_i2s_chan)) {
441 		dev_err(&pdev->dev, "No img,i2s-channels property\n");
442 		return -EINVAL;
443 	}
444 
445 	max_i2s_chan_pow_2 = 1 << get_count_order(i2s->max_i2s_chan);
446 
447 	i2s->channel_base = base + (max_i2s_chan_pow_2 * 0x20);
448 
449 	i2s->rst = devm_reset_control_get(&pdev->dev, "rst");
450 	if (IS_ERR(i2s->rst)) {
451 		if (PTR_ERR(i2s->rst) != -EPROBE_DEFER)
452 			dev_err(&pdev->dev, "No top level reset found\n");
453 		return PTR_ERR(i2s->rst);
454 	}
455 
456 	i2s->clk_sys = devm_clk_get(&pdev->dev, "sys");
457 	if (IS_ERR(i2s->clk_sys)) {
458 		if (PTR_ERR(i2s->clk_sys) != -EPROBE_DEFER)
459 			dev_err(dev, "Failed to acquire clock 'sys'\n");
460 		return PTR_ERR(i2s->clk_sys);
461 	}
462 
463 	i2s->clk_ref = devm_clk_get(&pdev->dev, "ref");
464 	if (IS_ERR(i2s->clk_ref)) {
465 		if (PTR_ERR(i2s->clk_ref) != -EPROBE_DEFER)
466 			dev_err(dev, "Failed to acquire clock 'ref'\n");
467 		return PTR_ERR(i2s->clk_ref);
468 	}
469 
470 	ret = clk_prepare_enable(i2s->clk_sys);
471 	if (ret)
472 		return ret;
473 
474 	reg = IMG_I2S_OUT_CTL_FRM_SIZE_MASK;
475 	img_i2s_out_writel(i2s, reg, IMG_I2S_OUT_CTL);
476 
477 	reg = IMG_I2S_OUT_CHAN_CTL_JUST_MASK |
478 		IMG_I2S_OUT_CHAN_CTL_LT_MASK |
479 		IMG_I2S_OUT_CHAN_CTL_CH_MASK |
480 		(8 << IMG_I2S_OUT_CHAN_CTL_FMT_SHIFT);
481 
482 	for (i = 0; i < i2s->max_i2s_chan; i++)
483 		img_i2s_out_ch_writel(i2s, i, reg, IMG_I2S_OUT_CH_CTL);
484 
485 	img_i2s_out_reset(i2s);
486 
487 	pm_runtime_enable(&pdev->dev);
488 	if (!pm_runtime_enabled(&pdev->dev)) {
489 		ret = img_i2s_out_resume(&pdev->dev);
490 		if (ret)
491 			goto err_pm_disable;
492 	}
493 
494 	i2s->active_channels = 1;
495 	i2s->dma_data.addr = res->start + IMG_I2S_OUT_TX_FIFO;
496 	i2s->dma_data.addr_width = 4;
497 	i2s->dma_data.maxburst = 4;
498 
499 	i2s->dai_driver.probe = img_i2s_out_dai_probe;
500 	i2s->dai_driver.playback.channels_min = 2;
501 	i2s->dai_driver.playback.channels_max = i2s->max_i2s_chan * 2;
502 	i2s->dai_driver.playback.rates = SNDRV_PCM_RATE_8000_192000;
503 	i2s->dai_driver.playback.formats = SNDRV_PCM_FMTBIT_S32_LE;
504 	i2s->dai_driver.ops = &img_i2s_out_dai_ops;
505 
506 	ret = devm_snd_soc_register_component(&pdev->dev,
507 			&img_i2s_out_component, &i2s->dai_driver, 1);
508 	if (ret)
509 		goto err_suspend;
510 
511 	ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
512 			&img_i2s_out_dma_config, 0);
513 	if (ret)
514 		goto err_suspend;
515 
516 	return 0;
517 
518 err_suspend:
519 	if (!pm_runtime_status_suspended(&pdev->dev))
520 		img_i2s_out_suspend(&pdev->dev);
521 err_pm_disable:
522 	pm_runtime_disable(&pdev->dev);
523 	clk_disable_unprepare(i2s->clk_sys);
524 
525 	return ret;
526 }
527 
528 static int img_i2s_out_dev_remove(struct platform_device *pdev)
529 {
530 	struct img_i2s_out *i2s = platform_get_drvdata(pdev);
531 
532 	pm_runtime_disable(&pdev->dev);
533 	if (!pm_runtime_status_suspended(&pdev->dev))
534 		img_i2s_out_suspend(&pdev->dev);
535 
536 	clk_disable_unprepare(i2s->clk_sys);
537 
538 	return 0;
539 }
540 
541 static const struct of_device_id img_i2s_out_of_match[] = {
542 	{ .compatible = "img,i2s-out" },
543 	{}
544 };
545 MODULE_DEVICE_TABLE(of, img_i2s_out_of_match);
546 
547 static const struct dev_pm_ops img_i2s_out_pm_ops = {
548 	SET_RUNTIME_PM_OPS(img_i2s_out_suspend,
549 			   img_i2s_out_resume, NULL)
550 };
551 
552 static struct platform_driver img_i2s_out_driver = {
553 	.driver = {
554 		.name = "img-i2s-out",
555 		.of_match_table = img_i2s_out_of_match,
556 		.pm = &img_i2s_out_pm_ops
557 	},
558 	.probe = img_i2s_out_probe,
559 	.remove = img_i2s_out_dev_remove
560 };
561 module_platform_driver(img_i2s_out_driver);
562 
563 MODULE_AUTHOR("Damien Horsley <Damien.Horsley@imgtec.com>");
564 MODULE_DESCRIPTION("IMG I2S Output Driver");
565 MODULE_LICENSE("GPL v2");
566