/* * Copyright 2012 Freescale Semiconductor, Inc. * Copyright 2012 Linaro Ltd. * * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License * Version 2 or later at the following locations: * * http://www.opensource.org/licenses/gpl-license.html * http://www.gnu.org/copyleft/gpl.html */ #include #include #include #include #include #include #include #include #include "imx-audmux.h" #define DAI_NAME_SIZE 32 #define MUX_PORT_MAX 7 struct imx_es8328_data { struct device *dev; struct snd_soc_dai_link dai; struct snd_soc_card card; char codec_dai_name[DAI_NAME_SIZE]; char platform_name[DAI_NAME_SIZE]; int jack_gpio; }; static struct snd_soc_jack_gpio headset_jack_gpios[] = { { .gpio = -1, .name = "headset-gpio", .report = SND_JACK_HEADSET, .invert = 0, .debounce_time = 200, }, }; static struct snd_soc_jack headset_jack; static int imx_es8328_dai_init(struct snd_soc_pcm_runtime *rtd) { struct imx_es8328_data *data = container_of(rtd->card, struct imx_es8328_data, card); int ret = 0; /* Headphone jack detection */ if (gpio_is_valid(data->jack_gpio)) { ret = snd_soc_card_jack_new(rtd->card, "Headphone", SND_JACK_HEADPHONE | SND_JACK_BTN_0, &headset_jack, NULL, 0); if (ret) return ret; headset_jack_gpios[0].gpio = data->jack_gpio; ret = snd_soc_jack_add_gpios(&headset_jack, ARRAY_SIZE(headset_jack_gpios), headset_jack_gpios); } return ret; } static const struct snd_soc_dapm_widget imx_es8328_dapm_widgets[] = { SND_SOC_DAPM_MIC("Mic Jack", NULL), SND_SOC_DAPM_HP("Headphone", NULL), SND_SOC_DAPM_SPK("Speaker", NULL), SND_SOC_DAPM_REGULATOR_SUPPLY("audio-amp", 1, 0), }; static int imx_es8328_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct device_node *ssi_np = NULL, *codec_np = NULL; struct platform_device *ssi_pdev; struct imx_es8328_data *data; u32 int_port, ext_port; int ret; struct device *dev = &pdev->dev; ret = of_property_read_u32(np, "mux-int-port", &int_port); if (ret) { dev_err(dev, "mux-int-port missing or invalid\n"); goto fail; } if (int_port > MUX_PORT_MAX || int_port == 0) { dev_err(dev, "mux-int-port: hardware only has %d mux ports\n", MUX_PORT_MAX); goto fail; } ret = of_property_read_u32(np, "mux-ext-port", &ext_port); if (ret) { dev_err(dev, "mux-ext-port missing or invalid\n"); goto fail; } if (ext_port > MUX_PORT_MAX || ext_port == 0) { dev_err(dev, "mux-ext-port: hardware only has %d mux ports\n", MUX_PORT_MAX); ret = -EINVAL; goto fail; } /* * The port numbering in the hardware manual starts at 1, while * the audmux API expects it starts at 0. */ int_port--; ext_port--; ret = imx_audmux_v2_configure_port(int_port, IMX_AUDMUX_V2_PTCR_SYN | IMX_AUDMUX_V2_PTCR_TFSEL(ext_port) | IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) | IMX_AUDMUX_V2_PTCR_TFSDIR | IMX_AUDMUX_V2_PTCR_TCLKDIR, IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port)); if (ret) { dev_err(dev, "audmux internal port setup failed\n"); return ret; } ret = imx_audmux_v2_configure_port(ext_port, IMX_AUDMUX_V2_PTCR_SYN, IMX_AUDMUX_V2_PDCR_RXDSEL(int_port)); if (ret) { dev_err(dev, "audmux external port setup failed\n"); return ret; } ssi_np = of_parse_phandle(pdev->dev.of_node, "ssi-controller", 0); codec_np = of_parse_phandle(pdev->dev.of_node, "audio-codec", 0); if (!ssi_np || !codec_np) { dev_err(dev, "phandle missing or invalid\n"); ret = -EINVAL; goto fail; } ssi_pdev = of_find_device_by_node(ssi_np); if (!ssi_pdev) { dev_err(dev, "failed to find SSI platform device\n"); ret = -EINVAL; goto fail; } data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); if (!data) { ret = -ENOMEM; goto fail; } data->dev = dev; data->jack_gpio = of_get_named_gpio(pdev->dev.of_node, "jack-gpio", 0); data->dai.name = "hifi"; data->dai.stream_name = "hifi"; data->dai.codec_dai_name = "es8328-hifi-analog"; data->dai.codec_of_node = codec_np; data->dai.cpu_of_node = ssi_np; data->dai.platform_of_node = ssi_np; data->dai.init = &imx_es8328_dai_init; data->dai.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | SND_SOC_DAIFMT_CBM_CFM; data->card.dev = dev; data->card.dapm_widgets = imx_es8328_dapm_widgets; data->card.num_dapm_widgets = ARRAY_SIZE(imx_es8328_dapm_widgets); ret = snd_soc_of_parse_card_name(&data->card, "model"); if (ret) { dev_err(dev, "Unable to parse card name\n"); goto fail; } ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing"); if (ret) { dev_err(dev, "Unable to parse routing: %d\n", ret); goto fail; } data->card.num_links = 1; data->card.owner = THIS_MODULE; data->card.dai_link = &data->dai; ret = snd_soc_register_card(&data->card); if (ret) { dev_err(dev, "Unable to register: %d\n", ret); goto fail; } platform_set_drvdata(pdev, data); fail: of_node_put(ssi_np); of_node_put(codec_np); return ret; } static int imx_es8328_remove(struct platform_device *pdev) { struct imx_es8328_data *data = platform_get_drvdata(pdev); snd_soc_jack_free_gpios(&headset_jack, ARRAY_SIZE(headset_jack_gpios), headset_jack_gpios); snd_soc_unregister_card(&data->card); return 0; } static const struct of_device_id imx_es8328_dt_ids[] = { { .compatible = "fsl,imx-audio-es8328", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, imx_es8328_dt_ids); static struct platform_driver imx_es8328_driver = { .driver = { .name = "imx-es8328", .of_match_table = imx_es8328_dt_ids, }, .probe = imx_es8328_probe, .remove = imx_es8328_remove, }; module_platform_driver(imx_es8328_driver); MODULE_AUTHOR("Sean Cross "); MODULE_DESCRIPTION("Kosagi i.MX6 ES8328 ASoC machine driver"); MODULE_LICENSE("GPL v2"); MODULE_ALIAS("platform:imx-audio-es8328"); function/key via ethtool ... 2017-02-01tcp: fix 0 divide in __tcp_select_window()Eric Dumazet1-2/+4 syszkaller fuzzer was able to trigger a divide by zero, when TCP window scaling is not enabled. SO_RCVBUF can be used not only to increase sk_rcvbuf, also to decrease it below current receive buffers utilization. If mss is negative or 0, just return a zero TCP window. Signed-off-by: Eric Dumazet <edumazet@google.com> Reported-by: Dmitry Vyukov <dvyukov@google.com> Acked-by: Neal Cardwell <ncardwell@google.com> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-01ipv6: pointer math error in ip6_tnl_parse_tlv_enc_lim()Dan Carpenter1-1/+1 Casting is a high precedence operation but "off" and "i" are in terms of bytes so we need to have some parenthesis here. Fixes: fbfa743a9d2a ("ipv6: fix ip6_tnl_parse_tlv_enc_lim()") Signed-off-by: Dan Carpenter <dan.carpenter@oracle.com> Acked-by: Eric Dumazet <edumazet@google.com> Signed-off-by: David S. Miller <davem@davemloft.net> 2017-02-01net/sched: matchall: Fix configuration raceYotam Gigi1-82/+45 In the current version, the matchall internal state is split into two structs: cls_matchall_head and cls_matchall_filter. This makes little sense, as matchall instance supports only one filter, and there is no situation where one exists and the other does not. In addition, that led to some races when filter was deleted while packet was processed. Unify that two structs into one, thus simplifying the process of matchall creation and deletion. As a result, the new, delete and get callbacks have a dummy implementation where all the work is done in destroy and change callbacks, as was done in cls_cgroup. Fixes: bf3994d2ed31 ("net/sched: introduce Match-all classifier") Reported-by: Daniel Borkmann <daniel@iogearbox.net> Signed-off-by: Yotam Gigi <yotamg@mellanox.com> Acked-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>