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