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 <linux/slab.h> 24 #include <linux/module.h> 25 #include <sound/jack.h> 26 #include <sound/core.h> 27 28 static int jack_switch_types[SND_JACK_SWITCH_TYPES] = { 29 SW_HEADPHONE_INSERT, 30 SW_MICROPHONE_INSERT, 31 SW_LINEOUT_INSERT, 32 SW_JACK_PHYSICAL_INSERT, 33 SW_VIDEOOUT_INSERT, 34 SW_LINEIN_INSERT, 35 }; 36 37 static int snd_jack_dev_disconnect(struct snd_device *device) 38 { 39 struct snd_jack *jack = device->device_data; 40 41 if (!jack->input_dev) 42 return 0; 43 44 /* If the input device is registered with the input subsystem 45 * then we need to use a different deallocator. */ 46 if (jack->registered) 47 input_unregister_device(jack->input_dev); 48 else 49 input_free_device(jack->input_dev); 50 jack->input_dev = NULL; 51 return 0; 52 } 53 54 static int snd_jack_dev_free(struct snd_device *device) 55 { 56 struct snd_jack *jack = device->device_data; 57 58 if (jack->private_free) 59 jack->private_free(jack); 60 61 snd_jack_dev_disconnect(device); 62 63 kfree(jack->id); 64 kfree(jack); 65 66 return 0; 67 } 68 69 static int snd_jack_dev_register(struct snd_device *device) 70 { 71 struct snd_jack *jack = device->device_data; 72 struct snd_card *card = device->card; 73 int err, i; 74 75 snprintf(jack->name, sizeof(jack->name), "%s %s", 76 card->shortname, jack->id); 77 jack->input_dev->name = jack->name; 78 79 /* Default to the sound card device. */ 80 if (!jack->input_dev->dev.parent) 81 jack->input_dev->dev.parent = snd_card_get_device_link(card); 82 83 /* Add capabilities for any keys that are enabled */ 84 for (i = 0; i < ARRAY_SIZE(jack->key); i++) { 85 int testbit = SND_JACK_BTN_0 >> i; 86 87 if (!(jack->type & testbit)) 88 continue; 89 90 if (!jack->key[i]) 91 jack->key[i] = BTN_0 + i; 92 93 input_set_capability(jack->input_dev, EV_KEY, jack->key[i]); 94 } 95 96 err = input_register_device(jack->input_dev); 97 if (err == 0) 98 jack->registered = 1; 99 100 return err; 101 } 102 103 /** 104 * snd_jack_new - Create a new jack 105 * @card: the card instance 106 * @id: an identifying string for this jack 107 * @type: a bitmask of enum snd_jack_type values that can be detected by 108 * this jack 109 * @jjack: Used to provide the allocated jack object to the caller. 110 * 111 * Creates a new jack object. 112 * 113 * Return: Zero if successful, or a negative error code on failure. 114 * On success @jjack will be initialised. 115 */ 116 int snd_jack_new(struct snd_card *card, const char *id, int type, 117 struct snd_jack **jjack) 118 { 119 struct snd_jack *jack; 120 int err; 121 int i; 122 static struct snd_device_ops ops = { 123 .dev_free = snd_jack_dev_free, 124 .dev_register = snd_jack_dev_register, 125 .dev_disconnect = snd_jack_dev_disconnect, 126 }; 127 128 jack = kzalloc(sizeof(struct snd_jack), GFP_KERNEL); 129 if (jack == NULL) 130 return -ENOMEM; 131 132 jack->id = kstrdup(id, GFP_KERNEL); 133 134 jack->input_dev = input_allocate_device(); 135 if (jack->input_dev == NULL) { 136 err = -ENOMEM; 137 goto fail_input; 138 } 139 140 jack->input_dev->phys = "ALSA"; 141 142 jack->type = type; 143 144 for (i = 0; i < SND_JACK_SWITCH_TYPES; i++) 145 if (type & (1 << i)) 146 input_set_capability(jack->input_dev, EV_SW, 147 jack_switch_types[i]); 148 149 err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops); 150 if (err < 0) 151 goto fail_input; 152 153 *jjack = jack; 154 155 return 0; 156 157 fail_input: 158 input_free_device(jack->input_dev); 159 kfree(jack->id); 160 kfree(jack); 161 return err; 162 } 163 EXPORT_SYMBOL(snd_jack_new); 164 165 /** 166 * snd_jack_set_parent - Set the parent device for a jack 167 * 168 * @jack: The jack to configure 169 * @parent: The device to set as parent for the jack. 170 * 171 * Set the parent for the jack devices in the device tree. This 172 * function is only valid prior to registration of the jack. If no 173 * parent is configured then the parent device will be the sound card. 174 */ 175 void snd_jack_set_parent(struct snd_jack *jack, struct device *parent) 176 { 177 WARN_ON(jack->registered); 178 179 jack->input_dev->dev.parent = parent; 180 } 181 EXPORT_SYMBOL(snd_jack_set_parent); 182 183 /** 184 * snd_jack_set_key - Set a key mapping on a jack 185 * 186 * @jack: The jack to configure 187 * @type: Jack report type for this key 188 * @keytype: Input layer key type to be reported 189 * 190 * Map a SND_JACK_BTN_ button type to an input layer key, allowing 191 * reporting of keys on accessories via the jack abstraction. If no 192 * mapping is provided but keys are enabled in the jack type then 193 * BTN_n numeric buttons will be reported. 194 * 195 * If jacks are not reporting via the input API this call will have no 196 * effect. 197 * 198 * Note that this is intended to be use by simple devices with small 199 * numbers of keys that can be reported. It is also possible to 200 * access the input device directly - devices with complex input 201 * capabilities on accessories should consider doing this rather than 202 * using this abstraction. 203 * 204 * This function may only be called prior to registration of the jack. 205 * 206 * Return: Zero if successful, or a negative error code on failure. 207 */ 208 int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type, 209 int keytype) 210 { 211 int key = fls(SND_JACK_BTN_0) - fls(type); 212 213 WARN_ON(jack->registered); 214 215 if (!keytype || key >= ARRAY_SIZE(jack->key)) 216 return -EINVAL; 217 218 jack->type |= type; 219 jack->key[key] = keytype; 220 221 return 0; 222 } 223 EXPORT_SYMBOL(snd_jack_set_key); 224 225 /** 226 * snd_jack_report - Report the current status of a jack 227 * 228 * @jack: The jack to report status for 229 * @status: The current status of the jack 230 */ 231 void snd_jack_report(struct snd_jack *jack, int status) 232 { 233 int i; 234 235 if (!jack) 236 return; 237 238 for (i = 0; i < ARRAY_SIZE(jack->key); i++) { 239 int testbit = SND_JACK_BTN_0 >> i; 240 241 if (jack->type & testbit) 242 input_report_key(jack->input_dev, jack->key[i], 243 status & testbit); 244 } 245 246 for (i = 0; i < ARRAY_SIZE(jack_switch_types); i++) { 247 int testbit = 1 << i; 248 if (jack->type & testbit) 249 input_report_switch(jack->input_dev, 250 jack_switch_types[i], 251 status & testbit); 252 } 253 254 input_sync(jack->input_dev); 255 } 256 EXPORT_SYMBOL(snd_jack_report); 257 258 MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); 259 MODULE_DESCRIPTION("Jack detection support for ALSA"); 260 MODULE_LICENSE("GPL"); 261