/* * sound/soc/tegra/tegra_wired_jack.c * * Copyright (c) 2011, NVIDIA Corporation. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include #include #include #include "tegra_soc.h" #define HEAD_DET_GPIO 0 /* jack */ static struct snd_soc_jack *tegra_wired_jack; static struct snd_soc_jack_pin hs_jack_pins[] = { { .pin = "Headset Jack", .mask = SND_JACK_HEADSET, }, { .pin = "Headphone Jack", .mask = SND_JACK_HEADPHONE, }, { .pin = "Mic Jack", .mask = SND_JACK_MICROPHONE, }, }; static struct snd_soc_jack_gpio hs_jack_gpios[] = { { /* gpio pin depends on board traits */ .name = "headphone-detect-gpio", .report = SND_JACK_HEADPHONE, .invert = 1, .debounce_time = 200, }, }; static struct switch_dev wired_switch_dev = { .name = "h2w", }; static int wired_swith_notify(struct notifier_block *self, unsigned long action, void* dev) { int state = 0; switch (action) { case SND_JACK_HEADSET: state = 1; break; case SND_JACK_HEADPHONE: state = 2; break; default: state = 0; } switch_set_state(&wired_switch_dev, state); return NOTIFY_OK; } static struct notifier_block wired_switch_nb = { .notifier_call = wired_swith_notify, }; /* platform driver */ static int tegra_wired_jack_probe(struct platform_device *pdev) { int ret; int hp_det_n; struct tegra_wired_jack_conf *pdata; pdata = (struct tegra_wired_jack_conf *)pdev->dev.platform_data; if (!pdata || !pdata->hp_det_n) { pr_err("Please set up gpio pins for jack.\n"); return -EBUSY; } hp_det_n = pdata->hp_det_n; hs_jack_gpios[HEAD_DET_GPIO].gpio = hp_det_n; ret = snd_soc_jack_add_gpios(tegra_wired_jack, ARRAY_SIZE(hs_jack_gpios), hs_jack_gpios); if (ret) { pr_err("Could NOT set up gpio pins for jack.\n"); snd_soc_jack_free_gpios(tegra_wired_jack, ARRAY_SIZE(hs_jack_gpios), hs_jack_gpios); return ret; } return 0; } static int tegra_wired_jack_remove(struct platform_device *pdev) { snd_soc_jack_free_gpios(tegra_wired_jack, ARRAY_SIZE(hs_jack_gpios), hs_jack_gpios); return 0; } static struct platform_driver tegra_wired_jack_driver = { .probe = tegra_wired_jack_probe, .remove = __devexit_p(tegra_wired_jack_remove), .driver = { .name = "tegra_wired_jack", .owner = THIS_MODULE, }, }; int tegra_jack_init(struct snd_soc_codec *codec) { int ret; if (!codec) return -1; tegra_wired_jack = kzalloc(sizeof(*tegra_wired_jack), GFP_KERNEL); if (!tegra_wired_jack) { pr_err("failed to allocate tegra_wired_jack \n"); return -ENOMEM; } /* Add jack detection */ ret = snd_soc_jack_new(codec->socdev->card, "Headset Jack", SND_JACK_HEADSET, tegra_wired_jack); if (ret < 0) goto failed; ret = snd_soc_jack_add_pins(tegra_wired_jack, ARRAY_SIZE(hs_jack_pins), hs_jack_pins); if (ret < 0) goto failed; /* Addd h2w swith class support */ ret = switch_dev_register(&wired_switch_dev); if (ret < 0) goto switch_dev_failed; snd_soc_jack_notifier_register(tegra_wired_jack, &wired_switch_nb); ret = platform_driver_register(&tegra_wired_jack_driver); if (ret < 0) goto platform_dev_failed; return 0; switch_dev_failed: switch_dev_unregister(&wired_switch_dev); platform_dev_failed: platform_driver_unregister(&tegra_wired_jack_driver); failed: if (tegra_wired_jack) { kfree(tegra_wired_jack); tegra_wired_jack = 0; } return ret; } void tegra_jack_exit(void) { switch_dev_unregister(&wired_switch_dev); platform_driver_unregister(&tegra_wired_jack_driver); if (tegra_wired_jack) { kfree(tegra_wired_jack); tegra_wired_jack = 0; } }