Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
0002 //
0003 // Copyright (c) 2018 BayLibre, SAS.
0004 // Author: Jerome Brunet <jbrunet@baylibre.com>
0005 
0006 /*
0007  * This driver implements the frontend playback DAI of AXG and G12A based SoCs
0008  */
0009 
0010 #include <linux/clk.h>
0011 #include <linux/regmap.h>
0012 #include <linux/module.h>
0013 #include <linux/of_platform.h>
0014 #include <sound/pcm_params.h>
0015 #include <sound/soc.h>
0016 #include <sound/soc-dai.h>
0017 
0018 #include "axg-fifo.h"
0019 
0020 #define CTRL0_FRDDR_PP_MODE     BIT(30)
0021 #define CTRL0_SEL1_EN_SHIFT     3
0022 #define CTRL0_SEL2_SHIFT        4
0023 #define CTRL0_SEL2_EN_SHIFT     7
0024 #define CTRL0_SEL3_SHIFT        8
0025 #define CTRL0_SEL3_EN_SHIFT     11
0026 #define CTRL1_FRDDR_FORCE_FINISH    BIT(12)
0027 #define CTRL2_SEL1_SHIFT        0
0028 #define CTRL2_SEL1_EN_SHIFT     4
0029 #define CTRL2_SEL2_SHIFT        8
0030 #define CTRL2_SEL2_EN_SHIFT     12
0031 #define CTRL2_SEL3_SHIFT        16
0032 #define CTRL2_SEL3_EN_SHIFT     20
0033 
0034 static int g12a_frddr_dai_prepare(struct snd_pcm_substream *substream,
0035                   struct snd_soc_dai *dai)
0036 {
0037     struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai);
0038 
0039     /* Reset the read pointer to the FIFO_INIT_ADDR */
0040     regmap_update_bits(fifo->map, FIFO_CTRL1,
0041                CTRL1_FRDDR_FORCE_FINISH, 0);
0042     regmap_update_bits(fifo->map, FIFO_CTRL1,
0043                CTRL1_FRDDR_FORCE_FINISH, CTRL1_FRDDR_FORCE_FINISH);
0044     regmap_update_bits(fifo->map, FIFO_CTRL1,
0045                CTRL1_FRDDR_FORCE_FINISH, 0);
0046 
0047     return 0;
0048 }
0049 
0050 static int axg_frddr_dai_hw_params(struct snd_pcm_substream *substream,
0051                    struct snd_pcm_hw_params *params,
0052                    struct snd_soc_dai *dai)
0053 {
0054     struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai);
0055     unsigned int period, depth, val;
0056 
0057     period = params_period_bytes(params);
0058 
0059     /* Trim the FIFO depth if the period is small to improve latency */
0060     depth = min(period, fifo->depth);
0061     val = (depth / AXG_FIFO_BURST) - 1;
0062     regmap_update_bits(fifo->map, FIFO_CTRL1, CTRL1_FRDDR_DEPTH_MASK,
0063                CTRL1_FRDDR_DEPTH(val));
0064 
0065     return 0;
0066 }
0067 
0068 static int axg_frddr_dai_startup(struct snd_pcm_substream *substream,
0069                  struct snd_soc_dai *dai)
0070 {
0071     struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai);
0072     int ret;
0073 
0074     /* Enable pclk to access registers and clock the fifo ip */
0075     ret = clk_prepare_enable(fifo->pclk);
0076     if (ret)
0077         return ret;
0078 
0079     /* Apply single buffer mode to the interface */
0080     regmap_update_bits(fifo->map, FIFO_CTRL0, CTRL0_FRDDR_PP_MODE, 0);
0081 
0082     return 0;
0083 }
0084 
0085 static void axg_frddr_dai_shutdown(struct snd_pcm_substream *substream,
0086                    struct snd_soc_dai *dai)
0087 {
0088     struct axg_fifo *fifo = snd_soc_dai_get_drvdata(dai);
0089 
0090     clk_disable_unprepare(fifo->pclk);
0091 }
0092 
0093 static int axg_frddr_pcm_new(struct snd_soc_pcm_runtime *rtd,
0094                  struct snd_soc_dai *dai)
0095 {
0096     return axg_fifo_pcm_new(rtd, SNDRV_PCM_STREAM_PLAYBACK);
0097 }
0098 
0099 static const struct snd_soc_dai_ops axg_frddr_ops = {
0100     .hw_params  = axg_frddr_dai_hw_params,
0101     .startup    = axg_frddr_dai_startup,
0102     .shutdown   = axg_frddr_dai_shutdown,
0103 };
0104 
0105 static struct snd_soc_dai_driver axg_frddr_dai_drv = {
0106     .name = "FRDDR",
0107     .playback = {
0108         .stream_name    = "Playback",
0109         .channels_min   = 1,
0110         .channels_max   = AXG_FIFO_CH_MAX,
0111         .rates      = AXG_FIFO_RATES,
0112         .formats    = AXG_FIFO_FORMATS,
0113     },
0114     .ops        = &axg_frddr_ops,
0115     .pcm_new    = axg_frddr_pcm_new,
0116 };
0117 
0118 static const char * const axg_frddr_sel_texts[] = {
0119     "OUT 0", "OUT 1", "OUT 2", "OUT 3", "OUT 4", "OUT 5", "OUT 6", "OUT 7",
0120 };
0121 
0122 static SOC_ENUM_SINGLE_DECL(axg_frddr_sel_enum, FIFO_CTRL0, CTRL0_SEL_SHIFT,
0123                 axg_frddr_sel_texts);
0124 
0125 static const struct snd_kcontrol_new axg_frddr_out_demux =
0126     SOC_DAPM_ENUM("Output Sink", axg_frddr_sel_enum);
0127 
0128 static const struct snd_soc_dapm_widget axg_frddr_dapm_widgets[] = {
0129     SND_SOC_DAPM_DEMUX("SINK SEL", SND_SOC_NOPM, 0, 0,
0130                &axg_frddr_out_demux),
0131     SND_SOC_DAPM_AIF_OUT("OUT 0", NULL, 0, SND_SOC_NOPM, 0, 0),
0132     SND_SOC_DAPM_AIF_OUT("OUT 1", NULL, 0, SND_SOC_NOPM, 0, 0),
0133     SND_SOC_DAPM_AIF_OUT("OUT 2", NULL, 0, SND_SOC_NOPM, 0, 0),
0134     SND_SOC_DAPM_AIF_OUT("OUT 3", NULL, 0, SND_SOC_NOPM, 0, 0),
0135     SND_SOC_DAPM_AIF_OUT("OUT 4", NULL, 0, SND_SOC_NOPM, 0, 0),
0136     SND_SOC_DAPM_AIF_OUT("OUT 5", NULL, 0, SND_SOC_NOPM, 0, 0),
0137     SND_SOC_DAPM_AIF_OUT("OUT 6", NULL, 0, SND_SOC_NOPM, 0, 0),
0138     SND_SOC_DAPM_AIF_OUT("OUT 7", NULL, 0, SND_SOC_NOPM, 0, 0),
0139 };
0140 
0141 static const struct snd_soc_dapm_route axg_frddr_dapm_routes[] = {
0142     { "SINK SEL", NULL, "Playback" },
0143     { "OUT 0", "OUT 0",  "SINK SEL" },
0144     { "OUT 1", "OUT 1",  "SINK SEL" },
0145     { "OUT 2", "OUT 2",  "SINK SEL" },
0146     { "OUT 3", "OUT 3",  "SINK SEL" },
0147     { "OUT 4", "OUT 4",  "SINK SEL" },
0148     { "OUT 5", "OUT 5",  "SINK SEL" },
0149     { "OUT 6", "OUT 6",  "SINK SEL" },
0150     { "OUT 7", "OUT 7",  "SINK SEL" },
0151 };
0152 
0153 static const struct snd_soc_component_driver axg_frddr_component_drv = {
0154     .dapm_widgets       = axg_frddr_dapm_widgets,
0155     .num_dapm_widgets   = ARRAY_SIZE(axg_frddr_dapm_widgets),
0156     .dapm_routes        = axg_frddr_dapm_routes,
0157     .num_dapm_routes    = ARRAY_SIZE(axg_frddr_dapm_routes),
0158     .open           = axg_fifo_pcm_open,
0159     .close          = axg_fifo_pcm_close,
0160     .hw_params      = axg_fifo_pcm_hw_params,
0161     .hw_free        = axg_fifo_pcm_hw_free,
0162     .pointer        = axg_fifo_pcm_pointer,
0163     .trigger        = axg_fifo_pcm_trigger,
0164     .legacy_dai_naming  = 1,
0165 };
0166 
0167 static const struct axg_fifo_match_data axg_frddr_match_data = {
0168     .field_threshold    = REG_FIELD(FIFO_CTRL1, 16, 23),
0169     .component_drv      = &axg_frddr_component_drv,
0170     .dai_drv        = &axg_frddr_dai_drv
0171 };
0172 
0173 static const struct snd_soc_dai_ops g12a_frddr_ops = {
0174     .prepare    = g12a_frddr_dai_prepare,
0175     .hw_params  = axg_frddr_dai_hw_params,
0176     .startup    = axg_frddr_dai_startup,
0177     .shutdown   = axg_frddr_dai_shutdown,
0178 };
0179 
0180 static struct snd_soc_dai_driver g12a_frddr_dai_drv = {
0181     .name = "FRDDR",
0182     .playback = {
0183         .stream_name    = "Playback",
0184         .channels_min   = 1,
0185         .channels_max   = AXG_FIFO_CH_MAX,
0186         .rates      = AXG_FIFO_RATES,
0187         .formats    = AXG_FIFO_FORMATS,
0188     },
0189     .ops        = &g12a_frddr_ops,
0190     .pcm_new    = axg_frddr_pcm_new,
0191 };
0192 
0193 static SOC_ENUM_SINGLE_DECL(g12a_frddr_sel1_enum, FIFO_CTRL0, CTRL0_SEL_SHIFT,
0194                 axg_frddr_sel_texts);
0195 static SOC_ENUM_SINGLE_DECL(g12a_frddr_sel2_enum, FIFO_CTRL0, CTRL0_SEL2_SHIFT,
0196                 axg_frddr_sel_texts);
0197 static SOC_ENUM_SINGLE_DECL(g12a_frddr_sel3_enum, FIFO_CTRL0, CTRL0_SEL3_SHIFT,
0198                 axg_frddr_sel_texts);
0199 
0200 static const struct snd_kcontrol_new g12a_frddr_out1_demux =
0201     SOC_DAPM_ENUM("Output Src 1", g12a_frddr_sel1_enum);
0202 static const struct snd_kcontrol_new g12a_frddr_out2_demux =
0203     SOC_DAPM_ENUM("Output Src 2", g12a_frddr_sel2_enum);
0204 static const struct snd_kcontrol_new g12a_frddr_out3_demux =
0205     SOC_DAPM_ENUM("Output Src 3", g12a_frddr_sel3_enum);
0206 
0207 static const struct snd_kcontrol_new g12a_frddr_out1_enable =
0208     SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL0,
0209                     CTRL0_SEL1_EN_SHIFT, 1, 0);
0210 static const struct snd_kcontrol_new g12a_frddr_out2_enable =
0211     SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL0,
0212                     CTRL0_SEL2_EN_SHIFT, 1, 0);
0213 static const struct snd_kcontrol_new g12a_frddr_out3_enable =
0214     SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL0,
0215                     CTRL0_SEL3_EN_SHIFT, 1, 0);
0216 
0217 static const struct snd_soc_dapm_widget g12a_frddr_dapm_widgets[] = {
0218     SND_SOC_DAPM_AIF_OUT("SRC 1", NULL, 0, SND_SOC_NOPM, 0, 0),
0219     SND_SOC_DAPM_AIF_OUT("SRC 2", NULL, 0, SND_SOC_NOPM, 0, 0),
0220     SND_SOC_DAPM_AIF_OUT("SRC 3", NULL, 0, SND_SOC_NOPM, 0, 0),
0221     SND_SOC_DAPM_SWITCH("SRC 1 EN", SND_SOC_NOPM, 0, 0,
0222                 &g12a_frddr_out1_enable),
0223     SND_SOC_DAPM_SWITCH("SRC 2 EN", SND_SOC_NOPM, 0, 0,
0224                 &g12a_frddr_out2_enable),
0225     SND_SOC_DAPM_SWITCH("SRC 3 EN", SND_SOC_NOPM, 0, 0,
0226                 &g12a_frddr_out3_enable),
0227     SND_SOC_DAPM_DEMUX("SINK 1 SEL", SND_SOC_NOPM, 0, 0,
0228                &g12a_frddr_out1_demux),
0229     SND_SOC_DAPM_DEMUX("SINK 2 SEL", SND_SOC_NOPM, 0, 0,
0230                &g12a_frddr_out2_demux),
0231     SND_SOC_DAPM_DEMUX("SINK 3 SEL", SND_SOC_NOPM, 0, 0,
0232                &g12a_frddr_out3_demux),
0233     SND_SOC_DAPM_AIF_OUT("OUT 0", NULL, 0, SND_SOC_NOPM, 0, 0),
0234     SND_SOC_DAPM_AIF_OUT("OUT 1", NULL, 0, SND_SOC_NOPM, 0, 0),
0235     SND_SOC_DAPM_AIF_OUT("OUT 2", NULL, 0, SND_SOC_NOPM, 0, 0),
0236     SND_SOC_DAPM_AIF_OUT("OUT 3", NULL, 0, SND_SOC_NOPM, 0, 0),
0237     SND_SOC_DAPM_AIF_OUT("OUT 4", NULL, 0, SND_SOC_NOPM, 0, 0),
0238     SND_SOC_DAPM_AIF_OUT("OUT 5", NULL, 0, SND_SOC_NOPM, 0, 0),
0239     SND_SOC_DAPM_AIF_OUT("OUT 6", NULL, 0, SND_SOC_NOPM, 0, 0),
0240     SND_SOC_DAPM_AIF_OUT("OUT 7", NULL, 0, SND_SOC_NOPM, 0, 0),
0241 };
0242 
0243 static const struct snd_soc_dapm_route g12a_frddr_dapm_routes[] = {
0244     { "SRC 1", NULL, "Playback" },
0245     { "SRC 2", NULL, "Playback" },
0246     { "SRC 3", NULL, "Playback" },
0247     { "SRC 1 EN", "Switch", "SRC 1" },
0248     { "SRC 2 EN", "Switch", "SRC 2" },
0249     { "SRC 3 EN", "Switch", "SRC 3" },
0250     { "SINK 1 SEL", NULL, "SRC 1 EN" },
0251     { "SINK 2 SEL", NULL, "SRC 2 EN" },
0252     { "SINK 3 SEL", NULL, "SRC 3 EN" },
0253     { "OUT 0", "OUT 0", "SINK 1 SEL" },
0254     { "OUT 1", "OUT 1", "SINK 1 SEL" },
0255     { "OUT 2", "OUT 2", "SINK 1 SEL" },
0256     { "OUT 3", "OUT 3", "SINK 1 SEL" },
0257     { "OUT 4", "OUT 4", "SINK 1 SEL" },
0258     { "OUT 5", "OUT 5", "SINK 1 SEL" },
0259     { "OUT 6", "OUT 6", "SINK 1 SEL" },
0260     { "OUT 7", "OUT 7", "SINK 1 SEL" },
0261     { "OUT 0", "OUT 0", "SINK 2 SEL" },
0262     { "OUT 1", "OUT 1", "SINK 2 SEL" },
0263     { "OUT 2", "OUT 2", "SINK 2 SEL" },
0264     { "OUT 3", "OUT 3", "SINK 2 SEL" },
0265     { "OUT 4", "OUT 4", "SINK 2 SEL" },
0266     { "OUT 5", "OUT 5", "SINK 2 SEL" },
0267     { "OUT 6", "OUT 6", "SINK 2 SEL" },
0268     { "OUT 7", "OUT 7", "SINK 2 SEL" },
0269     { "OUT 0", "OUT 0", "SINK 3 SEL" },
0270     { "OUT 1", "OUT 1", "SINK 3 SEL" },
0271     { "OUT 2", "OUT 2", "SINK 3 SEL" },
0272     { "OUT 3", "OUT 3", "SINK 3 SEL" },
0273     { "OUT 4", "OUT 4", "SINK 3 SEL" },
0274     { "OUT 5", "OUT 5", "SINK 3 SEL" },
0275     { "OUT 6", "OUT 6", "SINK 3 SEL" },
0276     { "OUT 7", "OUT 7", "SINK 3 SEL" },
0277 };
0278 
0279 static const struct snd_soc_component_driver g12a_frddr_component_drv = {
0280     .dapm_widgets       = g12a_frddr_dapm_widgets,
0281     .num_dapm_widgets   = ARRAY_SIZE(g12a_frddr_dapm_widgets),
0282     .dapm_routes        = g12a_frddr_dapm_routes,
0283     .num_dapm_routes    = ARRAY_SIZE(g12a_frddr_dapm_routes),
0284     .open           = axg_fifo_pcm_open,
0285     .close          = axg_fifo_pcm_close,
0286     .hw_params      = g12a_fifo_pcm_hw_params,
0287     .hw_free        = axg_fifo_pcm_hw_free,
0288     .pointer        = axg_fifo_pcm_pointer,
0289     .trigger        = axg_fifo_pcm_trigger,
0290     .legacy_dai_naming  = 1,
0291 };
0292 
0293 static const struct axg_fifo_match_data g12a_frddr_match_data = {
0294     .field_threshold    = REG_FIELD(FIFO_CTRL1, 16, 23),
0295     .component_drv      = &g12a_frddr_component_drv,
0296     .dai_drv        = &g12a_frddr_dai_drv
0297 };
0298 
0299 /* On SM1, the output selection in on CTRL2 */
0300 static const struct snd_kcontrol_new sm1_frddr_out1_enable =
0301     SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL2,
0302                     CTRL2_SEL1_EN_SHIFT, 1, 0);
0303 static const struct snd_kcontrol_new sm1_frddr_out2_enable =
0304     SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL2,
0305                     CTRL2_SEL2_EN_SHIFT, 1, 0);
0306 static const struct snd_kcontrol_new sm1_frddr_out3_enable =
0307     SOC_DAPM_SINGLE_AUTODISABLE("Switch", FIFO_CTRL2,
0308                     CTRL2_SEL3_EN_SHIFT, 1, 0);
0309 
0310 static SOC_ENUM_SINGLE_DECL(sm1_frddr_sel1_enum, FIFO_CTRL2, CTRL2_SEL1_SHIFT,
0311                 axg_frddr_sel_texts);
0312 static SOC_ENUM_SINGLE_DECL(sm1_frddr_sel2_enum, FIFO_CTRL2, CTRL2_SEL2_SHIFT,
0313                 axg_frddr_sel_texts);
0314 static SOC_ENUM_SINGLE_DECL(sm1_frddr_sel3_enum, FIFO_CTRL2, CTRL2_SEL3_SHIFT,
0315                 axg_frddr_sel_texts);
0316 
0317 static const struct snd_kcontrol_new sm1_frddr_out1_demux =
0318     SOC_DAPM_ENUM("Output Src 1", sm1_frddr_sel1_enum);
0319 static const struct snd_kcontrol_new sm1_frddr_out2_demux =
0320     SOC_DAPM_ENUM("Output Src 2", sm1_frddr_sel2_enum);
0321 static const struct snd_kcontrol_new sm1_frddr_out3_demux =
0322     SOC_DAPM_ENUM("Output Src 3", sm1_frddr_sel3_enum);
0323 
0324 static const struct snd_soc_dapm_widget sm1_frddr_dapm_widgets[] = {
0325     SND_SOC_DAPM_AIF_OUT("SRC 1", NULL, 0, SND_SOC_NOPM, 0, 0),
0326     SND_SOC_DAPM_AIF_OUT("SRC 2", NULL, 0, SND_SOC_NOPM, 0, 0),
0327     SND_SOC_DAPM_AIF_OUT("SRC 3", NULL, 0, SND_SOC_NOPM, 0, 0),
0328     SND_SOC_DAPM_SWITCH("SRC 1 EN", SND_SOC_NOPM, 0, 0,
0329                 &sm1_frddr_out1_enable),
0330     SND_SOC_DAPM_SWITCH("SRC 2 EN", SND_SOC_NOPM, 0, 0,
0331                 &sm1_frddr_out2_enable),
0332     SND_SOC_DAPM_SWITCH("SRC 3 EN", SND_SOC_NOPM, 0, 0,
0333                 &sm1_frddr_out3_enable),
0334     SND_SOC_DAPM_DEMUX("SINK 1 SEL", SND_SOC_NOPM, 0, 0,
0335                &sm1_frddr_out1_demux),
0336     SND_SOC_DAPM_DEMUX("SINK 2 SEL", SND_SOC_NOPM, 0, 0,
0337                &sm1_frddr_out2_demux),
0338     SND_SOC_DAPM_DEMUX("SINK 3 SEL", SND_SOC_NOPM, 0, 0,
0339                &sm1_frddr_out3_demux),
0340     SND_SOC_DAPM_AIF_OUT("OUT 0", NULL, 0, SND_SOC_NOPM, 0, 0),
0341     SND_SOC_DAPM_AIF_OUT("OUT 1", NULL, 0, SND_SOC_NOPM, 0, 0),
0342     SND_SOC_DAPM_AIF_OUT("OUT 2", NULL, 0, SND_SOC_NOPM, 0, 0),
0343     SND_SOC_DAPM_AIF_OUT("OUT 3", NULL, 0, SND_SOC_NOPM, 0, 0),
0344     SND_SOC_DAPM_AIF_OUT("OUT 4", NULL, 0, SND_SOC_NOPM, 0, 0),
0345     SND_SOC_DAPM_AIF_OUT("OUT 5", NULL, 0, SND_SOC_NOPM, 0, 0),
0346     SND_SOC_DAPM_AIF_OUT("OUT 6", NULL, 0, SND_SOC_NOPM, 0, 0),
0347     SND_SOC_DAPM_AIF_OUT("OUT 7", NULL, 0, SND_SOC_NOPM, 0, 0),
0348 };
0349 
0350 static const struct snd_soc_component_driver sm1_frddr_component_drv = {
0351     .dapm_widgets       = sm1_frddr_dapm_widgets,
0352     .num_dapm_widgets   = ARRAY_SIZE(sm1_frddr_dapm_widgets),
0353     .dapm_routes        = g12a_frddr_dapm_routes,
0354     .num_dapm_routes    = ARRAY_SIZE(g12a_frddr_dapm_routes),
0355     .open           = axg_fifo_pcm_open,
0356     .close          = axg_fifo_pcm_close,
0357     .hw_params      = g12a_fifo_pcm_hw_params,
0358     .hw_free        = axg_fifo_pcm_hw_free,
0359     .pointer        = axg_fifo_pcm_pointer,
0360     .trigger        = axg_fifo_pcm_trigger,
0361     .legacy_dai_naming  = 1,
0362 };
0363 
0364 static const struct axg_fifo_match_data sm1_frddr_match_data = {
0365     .field_threshold    = REG_FIELD(FIFO_CTRL1, 16, 23),
0366     .component_drv      = &sm1_frddr_component_drv,
0367     .dai_drv        = &g12a_frddr_dai_drv
0368 };
0369 
0370 static const struct of_device_id axg_frddr_of_match[] = {
0371     {
0372         .compatible = "amlogic,axg-frddr",
0373         .data = &axg_frddr_match_data,
0374     }, {
0375         .compatible = "amlogic,g12a-frddr",
0376         .data = &g12a_frddr_match_data,
0377     }, {
0378         .compatible = "amlogic,sm1-frddr",
0379         .data = &sm1_frddr_match_data,
0380     }, {}
0381 };
0382 MODULE_DEVICE_TABLE(of, axg_frddr_of_match);
0383 
0384 static struct platform_driver axg_frddr_pdrv = {
0385     .probe = axg_fifo_probe,
0386     .driver = {
0387         .name = "axg-frddr",
0388         .of_match_table = axg_frddr_of_match,
0389     },
0390 };
0391 module_platform_driver(axg_frddr_pdrv);
0392 
0393 MODULE_DESCRIPTION("Amlogic AXG/G12A playback fifo driver");
0394 MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
0395 MODULE_LICENSE("GPL v2");