1 /* 2 * Jack abstraction layer 3 * 4 * Copyright 2008 Wolfson Microelectronics 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 * 20 */ 21 22 #include <linux/input.h> 23 #include <sound/jack.h> 24 #include <sound/core.h> 25 26 static int snd_jack_dev_free(struct snd_device *device) 27 { 28 struct snd_jack *jack = device->device_data; 29 30 /* If the input device is registered with the input subsystem 31 * then we need to use a different deallocator. */ 32 if (jack->registered) 33 input_unregister_device(jack->input_dev); 34 else 35 input_free_device(jack->input_dev); 36 37 kfree(jack->id); 38 kfree(jack); 39 40 return 0; 41 } 42 43 static int snd_jack_dev_register(struct snd_device *device) 44 { 45 struct snd_jack *jack = device->device_data; 46 struct snd_card *card = device->card; 47 int err; 48 49 snprintf(jack->name, sizeof(jack->name), "%s %s", 50 card->shortname, jack->id); 51 jack->input_dev->name = jack->name; 52 53 /* Default to the sound card device. */ 54 if (!jack->input_dev->dev.parent) 55 jack->input_dev->dev.parent = card->dev; 56 57 err = input_register_device(jack->input_dev); 58 if (err == 0) 59 jack->registered = 1; 60 61 return err; 62 } 63 64 /** 65 * snd_jack_new - Create a new jack 66 * @card: the card instance 67 * @id: an identifying string for this jack 68 * @type: a bitmask of enum snd_jack_type values that can be detected by 69 * this jack 70 * @jjack: Used to provide the allocated jack object to the caller. 71 * 72 * Creates a new jack object. 73 * 74 * Returns zero if successful, or a negative error code on failure. 75 * On success jjack will be initialised. 76 */ 77 int snd_jack_new(struct snd_card *card, const char *id, int type, 78 struct snd_jack **jjack) 79 { 80 struct snd_jack *jack; 81 int err; 82 static struct snd_device_ops ops = { 83 .dev_free = snd_jack_dev_free, 84 .dev_register = snd_jack_dev_register, 85 }; 86 87 jack = kzalloc(sizeof(struct snd_jack), GFP_KERNEL); 88 if (jack == NULL) 89 return -ENOMEM; 90 91 jack->id = kstrdup(id, GFP_KERNEL); 92 93 jack->input_dev = input_allocate_device(); 94 if (jack->input_dev == NULL) { 95 err = -ENOMEM; 96 goto fail_input; 97 } 98 99 jack->input_dev->phys = "ALSA"; 100 101 jack->type = type; 102 103 if (type & SND_JACK_HEADPHONE) 104 input_set_capability(jack->input_dev, EV_SW, 105 SW_HEADPHONE_INSERT); 106 if (type & SND_JACK_LINEOUT) 107 input_set_capability(jack->input_dev, EV_SW, 108 SW_LINEOUT_INSERT); 109 if (type & SND_JACK_MICROPHONE) 110 input_set_capability(jack->input_dev, EV_SW, 111 SW_MICROPHONE_INSERT); 112 if (type & SND_JACK_MECHANICAL) 113 input_set_capability(jack->input_dev, EV_SW, 114 SW_JACK_PHYSICAL_INSERT); 115 116 err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops); 117 if (err < 0) 118 goto fail_input; 119 120 *jjack = jack; 121 122 return 0; 123 124 fail_input: 125 input_free_device(jack->input_dev); 126 kfree(jack); 127 return err; 128 } 129 EXPORT_SYMBOL(snd_jack_new); 130 131 /** 132 * snd_jack_set_parent - Set the parent device for a jack 133 * 134 * @jack: The jack to configure 135 * @parent: The device to set as parent for the jack. 136 * 137 * Set the parent for the jack input device in the device tree. This 138 * function is only valid prior to registration of the jack. If no 139 * parent is configured then the parent device will be the sound card. 140 */ 141 void snd_jack_set_parent(struct snd_jack *jack, struct device *parent) 142 { 143 WARN_ON(jack->registered); 144 145 jack->input_dev->dev.parent = parent; 146 } 147 EXPORT_SYMBOL(snd_jack_set_parent); 148 149 /** 150 * snd_jack_report - Report the current status of a jack 151 * 152 * @jack: The jack to report status for 153 * @status: The current status of the jack 154 */ 155 void snd_jack_report(struct snd_jack *jack, int status) 156 { 157 if (!jack) 158 return; 159 160 if (jack->type & SND_JACK_HEADPHONE) 161 input_report_switch(jack->input_dev, SW_HEADPHONE_INSERT, 162 status & SND_JACK_HEADPHONE); 163 if (jack->type & SND_JACK_LINEOUT) 164 input_report_switch(jack->input_dev, SW_LINEOUT_INSERT, 165 status & SND_JACK_LINEOUT); 166 if (jack->type & SND_JACK_MICROPHONE) 167 input_report_switch(jack->input_dev, SW_MICROPHONE_INSERT, 168 status & SND_JACK_MICROPHONE); 169 if (jack->type & SND_JACK_MECHANICAL) 170 input_report_switch(jack->input_dev, SW_JACK_PHYSICAL_INSERT, 171 status & SND_JACK_MECHANICAL); 172 173 input_sync(jack->input_dev); 174 } 175 EXPORT_SYMBOL(snd_jack_report); 176 177 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 178 MODULE_DESCRIPTION("Jack detection support for ALSA"); 179 MODULE_LICENSE("GPL"); 180