Message ID | 1346148830-2965-1-git-send-email-gururaja.hebbar@ti.com (mailing list archive) |
---|---|
State | Awaiting Upstream |
Headers | show |
Hi, Gentle Ping. On Tue, Aug 28, 2012 at 15:43:50, Hebbar, Gururaja wrote: > Device tree support for Davinci Machine driver > > When the board boots with device tree, the driver will receive card, > codec, dai interface details (like the card name, DAPM routing map, > phandle for the audio components described in the dts file, codec mclk > speed). > The card will be set up based on this information. > Since the routing is provided via DT we can mark the card fully routed > so core can take care of disconnecting the unused pins. > > When here, code indentation and comment style is also fixed > > Signed-off-by: Hebbar, Gururaja <gururaja.hebbar@ti.com> > --- > Changes from V1: > - Change DT parameter from "ti,codec-clock" to "ti,codec-clock-rate" > - add more explanation to DT parameters > > Changes from V2: > - Update Documentation details > - Remove irrelevant commit message > > :000000 100644 0000000... b248014... A Documentation/devicetree/bindings/sound/davinci-evm-audio.txt > :100644 100644 ab0ad45... ca2a547... M sound/soc/davinci/davinci-evm.c > .../bindings/sound/davinci-evm-audio.txt | 66 +++++++ > sound/soc/davinci/davinci-evm.c | 185 +++++++++++++++++-- > 2 files changed, 231 insertions(+), 20 deletions(-) > > diff --git a/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt b/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt > new file mode 100644 > index 0000000..b248014 > --- /dev/null > +++ b/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt > @@ -0,0 +1,66 @@ > +* Texas Instruments SoC audio setups with TLV320AIC3X Codec > + > +Required properties: > +- compatible : "ti,davinci-evm-audio" > +- ti,model : The user-visible name of this sound complex. > +- ti,audio-routing : A list of the connections between audio components. > + Each entry is a pair of strings, the first being the connection's sink, > + the second being the connection's source. Valid names for sources and > + sinks are the codec's pins, and the jacks on the board: > + > + Codec pins: > + > + * MIC3L > + * MIC3R > + * LINE1L > + * LINE2L > + * LINE1R > + * LINE2R > + > + Board connectors: > + > + * Headphone Jack > + * Line Out > + * Mic Jack > + > +- ti,mcasp-controller : The phandle of the McASP controller > +- ti,audio-codec : The phandle of the TLV320AIC3x audio codec > +- ti,codec-clock-rate : The Codec Clock rate (in Hz) applied to the Codec > + > +- dai-data : A list of DAI data used by SOC code to register > + DAI, Codecs platform. > + The string index "should" be as shown below. > +dai-data = > +"<DAI Name>", "<DAI Stream Name>", > +"<CODEC DAI Name>", "<true if evm_aic3x_init is required, else false>", > +"<evm ops required (evm_ops or evm_spdif_ops)>"; > + > +Here fields > +"<DAI Name>" : used to indicate the DAI Name > +"<DAI Stream Name>" : used to indicate the Stream Name > +"<CODEC DAI Name>" : used to bind the link between Codec DAI and ASOC DAI > + > +Machine related options > +"<true/false>" : Whether the machine specific initialization > + : evm_aic3x_init() is required > + > +"<evm ops required>" : Which hardware ops function is to be used. > + : (evm_ops or evm_spdif_ops) > + : use evm-spdif-ops if DAI is working in DIT mode > + : else use evm-ops. These ops setup hw param callbacks > + : which are used to setup CODEC/cpu DAI configuration > + : and codec system clock. > + > +Example: > + > +sound { > + compatible = "ti,davinci-evm-audio"; > + ti,model = "DA830 EVM"; > + ti,audio-codec = <&tlv320aic3x>; > + ti,mcasp-controller = <&mcasp1>; > + ti,codec-clock-rate = <12000000>; > + dai-data = > + "TLV320AIC3X", "AIC3X", > + "tlv320aic3x-hifi", "true", > + "evm-ops"; > +}; > diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c > index ab0ad45..ca2a547 100644 > --- a/sound/soc/davinci/davinci-evm.c > +++ b/sound/soc/davinci/davinci-evm.c > @@ -34,27 +34,38 @@ static int evm_hw_params(struct snd_pcm_substream *substream, > struct snd_soc_pcm_runtime *rtd = substream->private_data; > struct snd_soc_dai *codec_dai = rtd->codec_dai; > struct snd_soc_dai *cpu_dai = rtd->cpu_dai; > + struct snd_soc_codec *codec = rtd->codec; > + struct snd_soc_card *soc_card = codec->card; > + struct device_node *np = soc_card->dev->of_node; > int ret = 0; > unsigned sysclk; > > - /* ASP1 on DM355 EVM is clocked by an external oscillator */ > - if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm() || > - machine_is_davinci_dm365_evm()) > - sysclk = 27000000; > - > - /* ASP0 in DM6446 EVM is clocked by U55, as configured by > - * board-dm644x-evm.c using GPIOs from U18. There are six > - * options; here we "know" we use a 48 KHz sample rate. > - */ > - else if (machine_is_davinci_evm()) > - sysclk = 12288000; > - > - else if (machine_is_davinci_da830_evm() || > - machine_is_davinci_da850_evm()) > - sysclk = 24576000; > - > - else > - return -EINVAL; > + if (np) { > + ret = of_property_read_u32(np, "ti,codec-clock-rate", &sysclk); > + if (ret < 0) > + return ret; > + } else { > + /* ASP1 on DM355 EVM is clocked by an external oscillator */ > + if (machine_is_davinci_dm355_evm() || > + machine_is_davinci_dm6467_evm() || > + machine_is_davinci_dm365_evm()) > + sysclk = 27000000; > + > + /* > + * ASP0 in DM6446 EVM is clocked by U55, as configured by > + * board-dm644x-evm.c using GPIOs from U18. There are six > + * options; here we "know" we use a 48 KHz sample rate. > + */ > + else if (machine_is_davinci_evm()) > + sysclk = 12288000; > + > + else if (machine_is_davinci_da830_evm() || > + machine_is_davinci_da850_evm()) > + sysclk = 24576000; > + > + else > + return -EINVAL; > + } > > /* set codec DAI configuration */ > ret = snd_soc_dai_set_fmt(codec_dai, AUDIO_FORMAT); > @@ -127,13 +138,22 @@ static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd) > { > struct snd_soc_codec *codec = rtd->codec; > struct snd_soc_dapm_context *dapm = &codec->dapm; > + struct device_node *np = codec->card->dev->of_node; > + int ret; > > /* Add davinci-evm specific widgets */ > snd_soc_dapm_new_controls(dapm, aic3x_dapm_widgets, > ARRAY_SIZE(aic3x_dapm_widgets)); > > - /* Set up davinci-evm specific audio path audio_map */ > - snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); > + if (np) { > + ret = snd_soc_of_parse_audio_routing(codec->card, > + "ti,audio-routing"); > + if (ret) > + return ret; > + } else { > + /* Set up davinci-evm specific audio path audio_map */ > + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); > + } > > /* not connected */ > snd_soc_dapm_disable_pin(dapm, "MONO_LOUT"); > @@ -282,6 +302,113 @@ static struct snd_soc_card da850_snd_soc_card = { > .num_links = 1, > }; > > +#if defined(CONFIG_OF) > +/* > + * This struct is just used as place holder. It will be filled with > + * data from dt node > + */ > +static struct snd_soc_dai_link evm_dai = { > +}; > + > +/* davinci evm audio machine driver */ > +static struct snd_soc_card evm_soc_card = { > + .owner = THIS_MODULE, > + .dai_link = &evm_dai, > + .num_links = 1, > +}; > + > +static int davinci_evm_probe(struct platform_device *pdev) > +{ > + struct device_node *np = pdev->dev.of_node; > + const char *stringPtr, *propname; > + u32 val; > + int ret = 0; > + > + propname = "dai-data"; > + val = of_property_count_strings(np, propname); > + if (val < 0) > + return val; > + > + ret = of_property_read_string_index(np, propname, 0, &evm_dai.name); > + if (ret < 0) > + return ret; > + > + ret = of_property_read_string_index(np, propname, 1, > + &evm_dai.stream_name); > + if (ret < 0) > + return ret; > + > + ret = of_property_read_string_index(np, propname, 2, > + &evm_dai.codec_dai_name); > + if (ret < 0) > + return ret; > + > + ret = of_property_read_string_index(np, propname, 3, &stringPtr); > + if (ret < 0) > + return ret; > + else > + if (strcasecmp(stringPtr, "true") == 0) > + evm_dai.init = evm_aic3x_init; > + > + ret = of_property_read_string_index(np, propname, 4, &stringPtr); > + if (ret < 0) { > + return ret; > + } else { > + if (strcasecmp(stringPtr, "evm-ops") == 0) > + evm_dai.ops = &evm_ops; > + else if (strcasecmp(stringPtr, "evm-spdif-ops") == 0) > + evm_dai.ops = &evm_spdif_ops; > + } > + > + evm_dai.codec_of_node = of_parse_phandle(np, "ti,audio-codec", 0); > + if (!evm_dai.codec_of_node) > + return -EINVAL; > + > + evm_dai.cpu_dai_of_node = of_parse_phandle(np, > + "ti,mcasp-controller", 0); > + if (!evm_dai.cpu_dai_of_node) > + return -EINVAL; > + > + evm_dai.platform_of_node = evm_dai.cpu_dai_of_node; > + > + evm_soc_card.dev = &pdev->dev; > + ret = snd_soc_of_parse_card_name(&evm_soc_card, "ti,model"); > + if (ret) > + return ret; > + > + ret = snd_soc_register_card(&evm_soc_card); > + if (ret) > + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); > + > + return ret; > +} > + > +static int __devexit davinci_evm_remove(struct platform_device *pdev) > +{ > + struct snd_soc_card *card = platform_get_drvdata(pdev); > + > + snd_soc_unregister_card(card); > + > + return 0; > +} > + > +static const struct of_device_id davinci_evm_dt_ids[] = { > + { .compatible = "ti,davinci-evm-audio", }, > + { /* sentinel */ } > +}; > +MODULE_DEVICE_TABLE(of, davinci_mcasp_dt_ids); > + > +static struct platform_driver davinci_evm_driver = { > + .probe = davinci_evm_probe, > + .remove = __devexit_p(davinci_evm_remove), > + .driver = { > + .name = "davinci_evm", > + .owner = THIS_MODULE, > + .of_match_table = of_match_ptr(davinci_evm_dt_ids), > + }, > +}; > +#endif > + > static struct platform_device *evm_snd_device; > > static int __init evm_init(void) > @@ -289,6 +416,14 @@ static int __init evm_init(void) > struct snd_soc_card *evm_snd_dev_data; > int index; > int ret; > +#if defined(CONFIG_OF) > + struct device_node *np; > + > + np = of_find_compatible_node(NULL, NULL, "ti,davinci-evm-audio"); > + if (np) { > + return platform_driver_register(&davinci_evm_driver); > + } > +#endif > > if (machine_is_davinci_evm()) { > evm_snd_dev_data = &dm6446_snd_soc_card_evm; > @@ -325,6 +460,16 @@ static int __init evm_init(void) > > static void __exit evm_exit(void) > { > +#if defined(CONFIG_OF) > + struct device_node *np; > + > + np = of_find_compatible_node(NULL, NULL, "ti,davinci-evm-audio"); > + if (np) { > + platform_driver_unregister(&davinci_evm_driver); > + return; > + } > +#endif > + > platform_device_unregister(evm_snd_device); > } > > -- > 1.7.1 > > Regards, Gururaja
Gentle Ping. Any updates about this version or can it be pulled into the tree. On Thu, Aug 30, 2012 at 09:29:55, Hebbar, Gururaja wrote: > > Hi, > > Gentle Ping. > > > On Tue, Aug 28, 2012 at 15:43:50, Hebbar, Gururaja wrote: > > Device tree support for Davinci Machine driver > > > > When the board boots with device tree, the driver will receive card, > > codec, dai interface details (like the card name, DAPM routing map, > > phandle for the audio components described in the dts file, codec mclk > > speed). > > The card will be set up based on this information. > > Since the routing is provided via DT we can mark the card fully routed > > so core can take care of disconnecting the unused pins. > > > > When here, code indentation and comment style is also fixed > > > > Signed-off-by: Hebbar, Gururaja <gururaja.hebbar@ti.com> > > --- > > Changes from V1: > > - Change DT parameter from "ti,codec-clock" to "ti,codec-clock-rate" > > - add more explanation to DT parameters > > > > Changes from V2: > > - Update Documentation details > > - Remove irrelevant commit message > > > > :000000 100644 0000000... b248014... A Documentation/devicetree/bindings/sound/davinci-evm-audio.txt > > :100644 100644 ab0ad45... ca2a547... M sound/soc/davinci/davinci-evm.c > > .../bindings/sound/davinci-evm-audio.txt | 66 +++++++ > > sound/soc/davinci/davinci-evm.c | 185 +++++++++++++++++-- > > 2 files changed, 231 insertions(+), 20 deletions(-) > > > > diff --git a/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt b/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt > > new file mode 100644 > > index 0000000..b248014 > > --- /dev/null > > +++ b/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt > > @@ -0,0 +1,66 @@ > > +* Texas Instruments SoC audio setups with TLV320AIC3X Codec > > + > > +Required properties: > > +- compatible : "ti,davinci-evm-audio" > > +- ti,model : The user-visible name of this sound complex. > > +- ti,audio-routing : A list of the connections between audio components. > > + Each entry is a pair of strings, the first being the connection's sink, > > + the second being the connection's source. Valid names for sources and > > + sinks are the codec's pins, and the jacks on the board: > > + > > + Codec pins: > > + > > + * MIC3L > > + * MIC3R > > + * LINE1L > > + * LINE2L > > + * LINE1R > > + * LINE2R > > + > > + Board connectors: > > + > > + * Headphone Jack > > + * Line Out > > + * Mic Jack > > + > > +- ti,mcasp-controller : The phandle of the McASP controller > > +- ti,audio-codec : The phandle of the TLV320AIC3x audio codec > > +- ti,codec-clock-rate : The Codec Clock rate (in Hz) applied to the Codec > > + > > +- dai-data : A list of DAI data used by SOC code to register > > + DAI, Codecs platform. > > + The string index "should" be as shown below. > > +dai-data = > > +"<DAI Name>", "<DAI Stream Name>", > > +"<CODEC DAI Name>", "<true if evm_aic3x_init is required, else false>", > > +"<evm ops required (evm_ops or evm_spdif_ops)>"; > > + > > +Here fields > > +"<DAI Name>" : used to indicate the DAI Name > > +"<DAI Stream Name>" : used to indicate the Stream Name > > +"<CODEC DAI Name>" : used to bind the link between Codec DAI and ASOC DAI > > + > > +Machine related options > > +"<true/false>" : Whether the machine specific initialization > > + : evm_aic3x_init() is required > > + > > +"<evm ops required>" : Which hardware ops function is to be used. > > + : (evm_ops or evm_spdif_ops) > > + : use evm-spdif-ops if DAI is working in DIT mode > > + : else use evm-ops. These ops setup hw param callbacks > > + : which are used to setup CODEC/cpu DAI configuration > > + : and codec system clock. > > + > > +Example: > > + > > +sound { > > + compatible = "ti,davinci-evm-audio"; > > + ti,model = "DA830 EVM"; > > + ti,audio-codec = <&tlv320aic3x>; > > + ti,mcasp-controller = <&mcasp1>; > > + ti,codec-clock-rate = <12000000>; > > + dai-data = > > + "TLV320AIC3X", "AIC3X", > > + "tlv320aic3x-hifi", "true", > > + "evm-ops"; > > +}; > > diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c > > index ab0ad45..ca2a547 100644 > > --- a/sound/soc/davinci/davinci-evm.c > > +++ b/sound/soc/davinci/davinci-evm.c > > @@ -34,27 +34,38 @@ static int evm_hw_params(struct snd_pcm_substream *substream, > > struct snd_soc_pcm_runtime *rtd = substream->private_data; > > struct snd_soc_dai *codec_dai = rtd->codec_dai; > > struct snd_soc_dai *cpu_dai = rtd->cpu_dai; > > + struct snd_soc_codec *codec = rtd->codec; > > + struct snd_soc_card *soc_card = codec->card; > > + struct device_node *np = soc_card->dev->of_node; > > int ret = 0; > > unsigned sysclk; > > > > - /* ASP1 on DM355 EVM is clocked by an external oscillator */ > > - if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm() || > > - machine_is_davinci_dm365_evm()) > > - sysclk = 27000000; > > - > > - /* ASP0 in DM6446 EVM is clocked by U55, as configured by > > - * board-dm644x-evm.c using GPIOs from U18. There are six > > - * options; here we "know" we use a 48 KHz sample rate. > > - */ > > - else if (machine_is_davinci_evm()) > > - sysclk = 12288000; > > - > > - else if (machine_is_davinci_da830_evm() || > > - machine_is_davinci_da850_evm()) > > - sysclk = 24576000; > > - > > - else > > - return -EINVAL; > > + if (np) { > > + ret = of_property_read_u32(np, "ti,codec-clock-rate", &sysclk); > > + if (ret < 0) > > + return ret; > > + } else { > > + /* ASP1 on DM355 EVM is clocked by an external oscillator */ > > + if (machine_is_davinci_dm355_evm() || > > + machine_is_davinci_dm6467_evm() || > > + machine_is_davinci_dm365_evm()) > > + sysclk = 27000000; > > + > > + /* > > + * ASP0 in DM6446 EVM is clocked by U55, as configured by > > + * board-dm644x-evm.c using GPIOs from U18. There are six > > + * options; here we "know" we use a 48 KHz sample rate. > > + */ > > + else if (machine_is_davinci_evm()) > > + sysclk = 12288000; > > + > > + else if (machine_is_davinci_da830_evm() || > > + machine_is_davinci_da850_evm()) > > + sysclk = 24576000; > > + > > + else > > + return -EINVAL; > > + } > > > > /* set codec DAI configuration */ > > ret = snd_soc_dai_set_fmt(codec_dai, AUDIO_FORMAT); > > @@ -127,13 +138,22 @@ static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd) > > { > > struct snd_soc_codec *codec = rtd->codec; > > struct snd_soc_dapm_context *dapm = &codec->dapm; > > + struct device_node *np = codec->card->dev->of_node; > > + int ret; > > > > /* Add davinci-evm specific widgets */ > > snd_soc_dapm_new_controls(dapm, aic3x_dapm_widgets, > > ARRAY_SIZE(aic3x_dapm_widgets)); > > > > - /* Set up davinci-evm specific audio path audio_map */ > > - snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); > > + if (np) { > > + ret = snd_soc_of_parse_audio_routing(codec->card, > > + "ti,audio-routing"); > > + if (ret) > > + return ret; > > + } else { > > + /* Set up davinci-evm specific audio path audio_map */ > > + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); > > + } > > > > /* not connected */ > > snd_soc_dapm_disable_pin(dapm, "MONO_LOUT"); > > @@ -282,6 +302,113 @@ static struct snd_soc_card da850_snd_soc_card = { > > .num_links = 1, > > }; > > > > +#if defined(CONFIG_OF) > > +/* > > + * This struct is just used as place holder. It will be filled with > > + * data from dt node > > + */ > > +static struct snd_soc_dai_link evm_dai = { > > +}; > > + > > +/* davinci evm audio machine driver */ > > +static struct snd_soc_card evm_soc_card = { > > + .owner = THIS_MODULE, > > + .dai_link = &evm_dai, > > + .num_links = 1, > > +}; > > + > > +static int davinci_evm_probe(struct platform_device *pdev) > > +{ > > + struct device_node *np = pdev->dev.of_node; > > + const char *stringPtr, *propname; > > + u32 val; > > + int ret = 0; > > + > > + propname = "dai-data"; > > + val = of_property_count_strings(np, propname); > > + if (val < 0) > > + return val; > > + > > + ret = of_property_read_string_index(np, propname, 0, &evm_dai.name); > > + if (ret < 0) > > + return ret; > > + > > + ret = of_property_read_string_index(np, propname, 1, > > + &evm_dai.stream_name); > > + if (ret < 0) > > + return ret; > > + > > + ret = of_property_read_string_index(np, propname, 2, > > + &evm_dai.codec_dai_name); > > + if (ret < 0) > > + return ret; > > + > > + ret = of_property_read_string_index(np, propname, 3, &stringPtr); > > + if (ret < 0) > > + return ret; > > + else > > + if (strcasecmp(stringPtr, "true") == 0) > > + evm_dai.init = evm_aic3x_init; > > + > > + ret = of_property_read_string_index(np, propname, 4, &stringPtr); > > + if (ret < 0) { > > + return ret; > > + } else { > > + if (strcasecmp(stringPtr, "evm-ops") == 0) > > + evm_dai.ops = &evm_ops; > > + else if (strcasecmp(stringPtr, "evm-spdif-ops") == 0) > > + evm_dai.ops = &evm_spdif_ops; > > + } > > + > > + evm_dai.codec_of_node = of_parse_phandle(np, "ti,audio-codec", 0); > > + if (!evm_dai.codec_of_node) > > + return -EINVAL; > > + > > + evm_dai.cpu_dai_of_node = of_parse_phandle(np, > > + "ti,mcasp-controller", 0); > > + if (!evm_dai.cpu_dai_of_node) > > + return -EINVAL; > > + > > + evm_dai.platform_of_node = evm_dai.cpu_dai_of_node; > > + > > + evm_soc_card.dev = &pdev->dev; > > + ret = snd_soc_of_parse_card_name(&evm_soc_card, "ti,model"); > > + if (ret) > > + return ret; > > + > > + ret = snd_soc_register_card(&evm_soc_card); > > + if (ret) > > + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); > > + > > + return ret; > > +} > > + > > +static int __devexit davinci_evm_remove(struct platform_device *pdev) > > +{ > > + struct snd_soc_card *card = platform_get_drvdata(pdev); > > + > > + snd_soc_unregister_card(card); > > + > > + return 0; > > +} > > + > > +static const struct of_device_id davinci_evm_dt_ids[] = { > > + { .compatible = "ti,davinci-evm-audio", }, > > + { /* sentinel */ } > > +}; > > +MODULE_DEVICE_TABLE(of, davinci_mcasp_dt_ids); > > + > > +static struct platform_driver davinci_evm_driver = { > > + .probe = davinci_evm_probe, > > + .remove = __devexit_p(davinci_evm_remove), > > + .driver = { > > + .name = "davinci_evm", > > + .owner = THIS_MODULE, > > + .of_match_table = of_match_ptr(davinci_evm_dt_ids), > > + }, > > +}; > > +#endif > > + > > static struct platform_device *evm_snd_device; > > > > static int __init evm_init(void) > > @@ -289,6 +416,14 @@ static int __init evm_init(void) > > struct snd_soc_card *evm_snd_dev_data; > > int index; > > int ret; > > +#if defined(CONFIG_OF) > > + struct device_node *np; > > + > > + np = of_find_compatible_node(NULL, NULL, "ti,davinci-evm-audio"); > > + if (np) { > > + return platform_driver_register(&davinci_evm_driver); > > + } > > +#endif > > > > if (machine_is_davinci_evm()) { > > evm_snd_dev_data = &dm6446_snd_soc_card_evm; > > @@ -325,6 +460,16 @@ static int __init evm_init(void) > > > > static void __exit evm_exit(void) > > { > > +#if defined(CONFIG_OF) > > + struct device_node *np; > > + > > + np = of_find_compatible_node(NULL, NULL, "ti,davinci-evm-audio"); > > + if (np) { > > + platform_driver_unregister(&davinci_evm_driver); > > + return; > > + } > > +#endif > > + > > platform_device_unregister(evm_snd_device); > > } > > > > -- > > 1.7.1 > > > > > > > Regards, > Gururaja > Regards, Gururaja
On Fri, Aug 31, 2012 at 08:55:39AM +0000, Hebbar, Gururaja wrote: > > Gentle Ping. Any updates about this version or can it be pulled into the tree. > > On Thu, Aug 30, 2012 at 09:29:55, Hebbar, Gururaja wrote: Please don't do this: - Never top post. - If the patch genuinely has been forgotten you need to resend it. - You've allowed less than 24 hours for a reply, this is unreasonable. Unless there's a critical bug fix you should only be worrying after a few weeks or so.
On Fri, Aug 31, 2012 at 22:57:30, Mark Brown wrote: > On Fri, Aug 31, 2012 at 08:55:39AM +0000, Hebbar, Gururaja wrote: > > > > Gentle Ping. Any updates about this version or can it be pulled into the tree. > > > > On Thu, Aug 30, 2012 at 09:29:55, Hebbar, Gururaja wrote: > > Please don't do this: > > - Never top post. > - If the patch genuinely has been forgotten you need to resend it. > - You've allowed less than 24 hours for a reply, this is unreasonable. > Unless there's a critical bug fix you should only be worrying after a > few weeks or so. > I am really sorry for this. I will not repeat this in the future. I was in a little hurry because there were some dependent patches and you wanted to clear the pipeline. Sorry again for the trouble. Regards, Gururaja
On Tue, Aug 28, 2012 at 15:43:50, Hebbar, Gururaja wrote: > Device tree support for Davinci Machine driver > > When the board boots with device tree, the driver will receive card, > codec, dai interface details (like the card name, DAPM routing map, > phandle for the audio components described in the dts file, codec mclk > speed). > The card will be set up based on this information. > Since the routing is provided via DT we can mark the card fully routed > so core can take care of disconnecting the unused pins. > > When here, code indentation and comment style is also fixed Gentle ping. Is there any comments/review for this patch? If not, can this be pulled in? > > Signed-off-by: Hebbar, Gururaja <gururaja.hebbar@ti.com> > --- > Changes from V1: > - Change DT parameter from "ti,codec-clock" to "ti,codec-clock-rate" > - add more explanation to DT parameters > > Changes from V2: > - Update Documentation details > - Remove irrelevant commit message > > :000000 100644 0000000... b248014... A Documentation/devicetree/bindings/sound/davinci-evm-audio.txt > :100644 100644 ab0ad45... ca2a547... M sound/soc/davinci/davinci-evm.c > .../bindings/sound/davinci-evm-audio.txt | 66 +++++++ > sound/soc/davinci/davinci-evm.c | 185 +++++++++++++++++-- > 2 files changed, 231 insertions(+), 20 deletions(-) > > diff --git a/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt b/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt > new file mode 100644 > index 0000000..b248014 > --- /dev/null > +++ b/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt > @@ -0,0 +1,66 @@ > +* Texas Instruments SoC audio setups with TLV320AIC3X Codec > + > +Required properties: > +- compatible : "ti,davinci-evm-audio" > +- ti,model : The user-visible name of this sound complex. > +- ti,audio-routing : A list of the connections between audio components. > + Each entry is a pair of strings, the first being the connection's sink, > + the second being the connection's source. Valid names for sources and > + sinks are the codec's pins, and the jacks on the board: > + > + Codec pins: > + > + * MIC3L > + * MIC3R > + * LINE1L > + * LINE2L > + * LINE1R > + * LINE2R > + > + Board connectors: > + > + * Headphone Jack > + * Line Out > + * Mic Jack > + > +- ti,mcasp-controller : The phandle of the McASP controller > +- ti,audio-codec : The phandle of the TLV320AIC3x audio codec > +- ti,codec-clock-rate : The Codec Clock rate (in Hz) applied to the Codec > + > +- dai-data : A list of DAI data used by SOC code to register > + DAI, Codecs platform. > + The string index "should" be as shown below. > +dai-data = > +"<DAI Name>", "<DAI Stream Name>", > +"<CODEC DAI Name>", "<true if evm_aic3x_init is required, else false>", > +"<evm ops required (evm_ops or evm_spdif_ops)>"; > + > +Here fields > +"<DAI Name>" : used to indicate the DAI Name > +"<DAI Stream Name>" : used to indicate the Stream Name > +"<CODEC DAI Name>" : used to bind the link between Codec DAI and ASOC DAI > + > +Machine related options > +"<true/false>" : Whether the machine specific initialization > + : evm_aic3x_init() is required > + > +"<evm ops required>" : Which hardware ops function is to be used. > + : (evm_ops or evm_spdif_ops) > + : use evm-spdif-ops if DAI is working in DIT mode > + : else use evm-ops. These ops setup hw param callbacks > + : which are used to setup CODEC/cpu DAI configuration > + : and codec system clock. > + > +Example: > + > +sound { > + compatible = "ti,davinci-evm-audio"; > + ti,model = "DA830 EVM"; > + ti,audio-codec = <&tlv320aic3x>; > + ti,mcasp-controller = <&mcasp1>; > + ti,codec-clock-rate = <12000000>; > + dai-data = > + "TLV320AIC3X", "AIC3X", > + "tlv320aic3x-hifi", "true", > + "evm-ops"; > +}; > diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c > index ab0ad45..ca2a547 100644 > --- a/sound/soc/davinci/davinci-evm.c > +++ b/sound/soc/davinci/davinci-evm.c > @@ -34,27 +34,38 @@ static int evm_hw_params(struct snd_pcm_substream *substream, > struct snd_soc_pcm_runtime *rtd = substream->private_data; > struct snd_soc_dai *codec_dai = rtd->codec_dai; > struct snd_soc_dai *cpu_dai = rtd->cpu_dai; > + struct snd_soc_codec *codec = rtd->codec; > + struct snd_soc_card *soc_card = codec->card; > + struct device_node *np = soc_card->dev->of_node; > int ret = 0; > unsigned sysclk; > > - /* ASP1 on DM355 EVM is clocked by an external oscillator */ > - if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm() || > - machine_is_davinci_dm365_evm()) > - sysclk = 27000000; > - > - /* ASP0 in DM6446 EVM is clocked by U55, as configured by > - * board-dm644x-evm.c using GPIOs from U18. There are six > - * options; here we "know" we use a 48 KHz sample rate. > - */ > - else if (machine_is_davinci_evm()) > - sysclk = 12288000; > - > - else if (machine_is_davinci_da830_evm() || > - machine_is_davinci_da850_evm()) > - sysclk = 24576000; > - > - else > - return -EINVAL; > + if (np) { > + ret = of_property_read_u32(np, "ti,codec-clock-rate", &sysclk); > + if (ret < 0) > + return ret; > + } else { > + /* ASP1 on DM355 EVM is clocked by an external oscillator */ > + if (machine_is_davinci_dm355_evm() || > + machine_is_davinci_dm6467_evm() || > + machine_is_davinci_dm365_evm()) > + sysclk = 27000000; > + > + /* > + * ASP0 in DM6446 EVM is clocked by U55, as configured by > + * board-dm644x-evm.c using GPIOs from U18. There are six > + * options; here we "know" we use a 48 KHz sample rate. > + */ > + else if (machine_is_davinci_evm()) > + sysclk = 12288000; > + > + else if (machine_is_davinci_da830_evm() || > + machine_is_davinci_da850_evm()) > + sysclk = 24576000; > + > + else > + return -EINVAL; > + } > > /* set codec DAI configuration */ > ret = snd_soc_dai_set_fmt(codec_dai, AUDIO_FORMAT); > @@ -127,13 +138,22 @@ static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd) > { > struct snd_soc_codec *codec = rtd->codec; > struct snd_soc_dapm_context *dapm = &codec->dapm; > + struct device_node *np = codec->card->dev->of_node; > + int ret; > > /* Add davinci-evm specific widgets */ > snd_soc_dapm_new_controls(dapm, aic3x_dapm_widgets, > ARRAY_SIZE(aic3x_dapm_widgets)); > > - /* Set up davinci-evm specific audio path audio_map */ > - snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); > + if (np) { > + ret = snd_soc_of_parse_audio_routing(codec->card, > + "ti,audio-routing"); > + if (ret) > + return ret; > + } else { > + /* Set up davinci-evm specific audio path audio_map */ > + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); > + } > > /* not connected */ > snd_soc_dapm_disable_pin(dapm, "MONO_LOUT"); > @@ -282,6 +302,113 @@ static struct snd_soc_card da850_snd_soc_card = { > .num_links = 1, > }; > > +#if defined(CONFIG_OF) > +/* > + * This struct is just used as place holder. It will be filled with > + * data from dt node > + */ > +static struct snd_soc_dai_link evm_dai = { > +}; > + > +/* davinci evm audio machine driver */ > +static struct snd_soc_card evm_soc_card = { > + .owner = THIS_MODULE, > + .dai_link = &evm_dai, > + .num_links = 1, > +}; > + > +static int davinci_evm_probe(struct platform_device *pdev) > +{ > + struct device_node *np = pdev->dev.of_node; > + const char *stringPtr, *propname; > + u32 val; > + int ret = 0; > + > + propname = "dai-data"; > + val = of_property_count_strings(np, propname); > + if (val < 0) > + return val; > + > + ret = of_property_read_string_index(np, propname, 0, &evm_dai.name); > + if (ret < 0) > + return ret; > + > + ret = of_property_read_string_index(np, propname, 1, > + &evm_dai.stream_name); > + if (ret < 0) > + return ret; > + > + ret = of_property_read_string_index(np, propname, 2, > + &evm_dai.codec_dai_name); > + if (ret < 0) > + return ret; > + > + ret = of_property_read_string_index(np, propname, 3, &stringPtr); > + if (ret < 0) > + return ret; > + else > + if (strcasecmp(stringPtr, "true") == 0) > + evm_dai.init = evm_aic3x_init; > + > + ret = of_property_read_string_index(np, propname, 4, &stringPtr); > + if (ret < 0) { > + return ret; > + } else { > + if (strcasecmp(stringPtr, "evm-ops") == 0) > + evm_dai.ops = &evm_ops; > + else if (strcasecmp(stringPtr, "evm-spdif-ops") == 0) > + evm_dai.ops = &evm_spdif_ops; > + } > + > + evm_dai.codec_of_node = of_parse_phandle(np, "ti,audio-codec", 0); > + if (!evm_dai.codec_of_node) > + return -EINVAL; > + > + evm_dai.cpu_dai_of_node = of_parse_phandle(np, > + "ti,mcasp-controller", 0); > + if (!evm_dai.cpu_dai_of_node) > + return -EINVAL; > + > + evm_dai.platform_of_node = evm_dai.cpu_dai_of_node; > + > + evm_soc_card.dev = &pdev->dev; > + ret = snd_soc_of_parse_card_name(&evm_soc_card, "ti,model"); > + if (ret) > + return ret; > + > + ret = snd_soc_register_card(&evm_soc_card); > + if (ret) > + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); > + > + return ret; > +} > + > +static int __devexit davinci_evm_remove(struct platform_device *pdev) > +{ > + struct snd_soc_card *card = platform_get_drvdata(pdev); > + > + snd_soc_unregister_card(card); > + > + return 0; > +} > + > +static const struct of_device_id davinci_evm_dt_ids[] = { > + { .compatible = "ti,davinci-evm-audio", }, > + { /* sentinel */ } > +}; > +MODULE_DEVICE_TABLE(of, davinci_mcasp_dt_ids); > + > +static struct platform_driver davinci_evm_driver = { > + .probe = davinci_evm_probe, > + .remove = __devexit_p(davinci_evm_remove), > + .driver = { > + .name = "davinci_evm", > + .owner = THIS_MODULE, > + .of_match_table = of_match_ptr(davinci_evm_dt_ids), > + }, > +}; > +#endif > + > static struct platform_device *evm_snd_device; > > static int __init evm_init(void) > @@ -289,6 +416,14 @@ static int __init evm_init(void) > struct snd_soc_card *evm_snd_dev_data; > int index; > int ret; > +#if defined(CONFIG_OF) > + struct device_node *np; > + > + np = of_find_compatible_node(NULL, NULL, "ti,davinci-evm-audio"); > + if (np) { > + return platform_driver_register(&davinci_evm_driver); > + } > +#endif > > if (machine_is_davinci_evm()) { > evm_snd_dev_data = &dm6446_snd_soc_card_evm; > @@ -325,6 +460,16 @@ static int __init evm_init(void) > > static void __exit evm_exit(void) > { > +#if defined(CONFIG_OF) > + struct device_node *np; > + > + np = of_find_compatible_node(NULL, NULL, "ti,davinci-evm-audio"); > + if (np) { > + platform_driver_unregister(&davinci_evm_driver); > + return; > + } > +#endif > + > platform_device_unregister(evm_snd_device); > } > > -- > 1.7.1 > > Regards, Gururaja
On Wed, Sep 12, 2012 at 07:55:08AM +0000, Hebbar, Gururaja wrote: > Gentle ping. Is there any comments/review for this patch? > If not, can this be pulled in? To repeat, don't send contentless pings. If the patch has genuinely been lost then it'll need a resend.
diff --git a/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt b/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt new file mode 100644 index 0000000..b248014 --- /dev/null +++ b/Documentation/devicetree/bindings/sound/davinci-evm-audio.txt @@ -0,0 +1,66 @@ +* Texas Instruments SoC audio setups with TLV320AIC3X Codec + +Required properties: +- compatible : "ti,davinci-evm-audio" +- ti,model : The user-visible name of this sound complex. +- ti,audio-routing : A list of the connections between audio components. + Each entry is a pair of strings, the first being the connection's sink, + the second being the connection's source. Valid names for sources and + sinks are the codec's pins, and the jacks on the board: + + Codec pins: + + * MIC3L + * MIC3R + * LINE1L + * LINE2L + * LINE1R + * LINE2R + + Board connectors: + + * Headphone Jack + * Line Out + * Mic Jack + +- ti,mcasp-controller : The phandle of the McASP controller +- ti,audio-codec : The phandle of the TLV320AIC3x audio codec +- ti,codec-clock-rate : The Codec Clock rate (in Hz) applied to the Codec + +- dai-data : A list of DAI data used by SOC code to register + DAI, Codecs platform. + The string index "should" be as shown below. +dai-data = +"<DAI Name>", "<DAI Stream Name>", +"<CODEC DAI Name>", "<true if evm_aic3x_init is required, else false>", +"<evm ops required (evm_ops or evm_spdif_ops)>"; + +Here fields +"<DAI Name>" : used to indicate the DAI Name +"<DAI Stream Name>" : used to indicate the Stream Name +"<CODEC DAI Name>" : used to bind the link between Codec DAI and ASOC DAI + +Machine related options +"<true/false>" : Whether the machine specific initialization + : evm_aic3x_init() is required + +"<evm ops required>" : Which hardware ops function is to be used. + : (evm_ops or evm_spdif_ops) + : use evm-spdif-ops if DAI is working in DIT mode + : else use evm-ops. These ops setup hw param callbacks + : which are used to setup CODEC/cpu DAI configuration + : and codec system clock. + +Example: + +sound { + compatible = "ti,davinci-evm-audio"; + ti,model = "DA830 EVM"; + ti,audio-codec = <&tlv320aic3x>; + ti,mcasp-controller = <&mcasp1>; + ti,codec-clock-rate = <12000000>; + dai-data = + "TLV320AIC3X", "AIC3X", + "tlv320aic3x-hifi", "true", + "evm-ops"; +}; diff --git a/sound/soc/davinci/davinci-evm.c b/sound/soc/davinci/davinci-evm.c index ab0ad45..ca2a547 100644 --- a/sound/soc/davinci/davinci-evm.c +++ b/sound/soc/davinci/davinci-evm.c @@ -34,27 +34,38 @@ static int evm_hw_params(struct snd_pcm_substream *substream, struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_dai *codec_dai = rtd->codec_dai; struct snd_soc_dai *cpu_dai = rtd->cpu_dai; + struct snd_soc_codec *codec = rtd->codec; + struct snd_soc_card *soc_card = codec->card; + struct device_node *np = soc_card->dev->of_node; int ret = 0; unsigned sysclk; - /* ASP1 on DM355 EVM is clocked by an external oscillator */ - if (machine_is_davinci_dm355_evm() || machine_is_davinci_dm6467_evm() || - machine_is_davinci_dm365_evm()) - sysclk = 27000000; - - /* ASP0 in DM6446 EVM is clocked by U55, as configured by - * board-dm644x-evm.c using GPIOs from U18. There are six - * options; here we "know" we use a 48 KHz sample rate. - */ - else if (machine_is_davinci_evm()) - sysclk = 12288000; - - else if (machine_is_davinci_da830_evm() || - machine_is_davinci_da850_evm()) - sysclk = 24576000; - - else - return -EINVAL; + if (np) { + ret = of_property_read_u32(np, "ti,codec-clock-rate", &sysclk); + if (ret < 0) + return ret; + } else { + /* ASP1 on DM355 EVM is clocked by an external oscillator */ + if (machine_is_davinci_dm355_evm() || + machine_is_davinci_dm6467_evm() || + machine_is_davinci_dm365_evm()) + sysclk = 27000000; + + /* + * ASP0 in DM6446 EVM is clocked by U55, as configured by + * board-dm644x-evm.c using GPIOs from U18. There are six + * options; here we "know" we use a 48 KHz sample rate. + */ + else if (machine_is_davinci_evm()) + sysclk = 12288000; + + else if (machine_is_davinci_da830_evm() || + machine_is_davinci_da850_evm()) + sysclk = 24576000; + + else + return -EINVAL; + } /* set codec DAI configuration */ ret = snd_soc_dai_set_fmt(codec_dai, AUDIO_FORMAT); @@ -127,13 +138,22 @@ static int evm_aic3x_init(struct snd_soc_pcm_runtime *rtd) { struct snd_soc_codec *codec = rtd->codec; struct snd_soc_dapm_context *dapm = &codec->dapm; + struct device_node *np = codec->card->dev->of_node; + int ret; /* Add davinci-evm specific widgets */ snd_soc_dapm_new_controls(dapm, aic3x_dapm_widgets, ARRAY_SIZE(aic3x_dapm_widgets)); - /* Set up davinci-evm specific audio path audio_map */ - snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); + if (np) { + ret = snd_soc_of_parse_audio_routing(codec->card, + "ti,audio-routing"); + if (ret) + return ret; + } else { + /* Set up davinci-evm specific audio path audio_map */ + snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map)); + } /* not connected */ snd_soc_dapm_disable_pin(dapm, "MONO_LOUT"); @@ -282,6 +302,113 @@ static struct snd_soc_card da850_snd_soc_card = { .num_links = 1, }; +#if defined(CONFIG_OF) +/* + * This struct is just used as place holder. It will be filled with + * data from dt node + */ +static struct snd_soc_dai_link evm_dai = { +}; + +/* davinci evm audio machine driver */ +static struct snd_soc_card evm_soc_card = { + .owner = THIS_MODULE, + .dai_link = &evm_dai, + .num_links = 1, +}; + +static int davinci_evm_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + const char *stringPtr, *propname; + u32 val; + int ret = 0; + + propname = "dai-data"; + val = of_property_count_strings(np, propname); + if (val < 0) + return val; + + ret = of_property_read_string_index(np, propname, 0, &evm_dai.name); + if (ret < 0) + return ret; + + ret = of_property_read_string_index(np, propname, 1, + &evm_dai.stream_name); + if (ret < 0) + return ret; + + ret = of_property_read_string_index(np, propname, 2, + &evm_dai.codec_dai_name); + if (ret < 0) + return ret; + + ret = of_property_read_string_index(np, propname, 3, &stringPtr); + if (ret < 0) + return ret; + else + if (strcasecmp(stringPtr, "true") == 0) + evm_dai.init = evm_aic3x_init; + + ret = of_property_read_string_index(np, propname, 4, &stringPtr); + if (ret < 0) { + return ret; + } else { + if (strcasecmp(stringPtr, "evm-ops") == 0) + evm_dai.ops = &evm_ops; + else if (strcasecmp(stringPtr, "evm-spdif-ops") == 0) + evm_dai.ops = &evm_spdif_ops; + } + + evm_dai.codec_of_node = of_parse_phandle(np, "ti,audio-codec", 0); + if (!evm_dai.codec_of_node) + return -EINVAL; + + evm_dai.cpu_dai_of_node = of_parse_phandle(np, + "ti,mcasp-controller", 0); + if (!evm_dai.cpu_dai_of_node) + return -EINVAL; + + evm_dai.platform_of_node = evm_dai.cpu_dai_of_node; + + evm_soc_card.dev = &pdev->dev; + ret = snd_soc_of_parse_card_name(&evm_soc_card, "ti,model"); + if (ret) + return ret; + + ret = snd_soc_register_card(&evm_soc_card); + if (ret) + dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret); + + return ret; +} + +static int __devexit davinci_evm_remove(struct platform_device *pdev) +{ + struct snd_soc_card *card = platform_get_drvdata(pdev); + + snd_soc_unregister_card(card); + + return 0; +} + +static const struct of_device_id davinci_evm_dt_ids[] = { + { .compatible = "ti,davinci-evm-audio", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, davinci_mcasp_dt_ids); + +static struct platform_driver davinci_evm_driver = { + .probe = davinci_evm_probe, + .remove = __devexit_p(davinci_evm_remove), + .driver = { + .name = "davinci_evm", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(davinci_evm_dt_ids), + }, +}; +#endif + static struct platform_device *evm_snd_device; static int __init evm_init(void) @@ -289,6 +416,14 @@ static int __init evm_init(void) struct snd_soc_card *evm_snd_dev_data; int index; int ret; +#if defined(CONFIG_OF) + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "ti,davinci-evm-audio"); + if (np) { + return platform_driver_register(&davinci_evm_driver); + } +#endif if (machine_is_davinci_evm()) { evm_snd_dev_data = &dm6446_snd_soc_card_evm; @@ -325,6 +460,16 @@ static int __init evm_init(void) static void __exit evm_exit(void) { +#if defined(CONFIG_OF) + struct device_node *np; + + np = of_find_compatible_node(NULL, NULL, "ti,davinci-evm-audio"); + if (np) { + platform_driver_unregister(&davinci_evm_driver); + return; + } +#endif + platform_device_unregister(evm_snd_device); }
Device tree support for Davinci Machine driver When the board boots with device tree, the driver will receive card, codec, dai interface details (like the card name, DAPM routing map, phandle for the audio components described in the dts file, codec mclk speed). The card will be set up based on this information. Since the routing is provided via DT we can mark the card fully routed so core can take care of disconnecting the unused pins. When here, code indentation and comment style is also fixed Signed-off-by: Hebbar, Gururaja <gururaja.hebbar@ti.com> --- Changes from V1: - Change DT parameter from "ti,codec-clock" to "ti,codec-clock-rate" - add more explanation to DT parameters Changes from V2: - Update Documentation details - Remove irrelevant commit message :000000 100644 0000000... b248014... A Documentation/devicetree/bindings/sound/davinci-evm-audio.txt :100644 100644 ab0ad45... ca2a547... M sound/soc/davinci/davinci-evm.c .../bindings/sound/davinci-evm-audio.txt | 66 +++++++ sound/soc/davinci/davinci-evm.c | 185 +++++++++++++++++-- 2 files changed, 231 insertions(+), 20 deletions(-)