2 * Retu/Vilma headset detection
4 * Copyright (C) 2006 Nokia Corporation
6 * Written by Juha Yrjölä
8 * This file is subject to the terms and conditions of the GNU General
9 * Public License. See the file "COPYING" in the main directory of this
10 * archive for more details.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include <linux/module.h>
23 #include <linux/init.h>
24 #include <linux/kernel.h>
25 #include <linux/delay.h>
26 #include <linux/input.h>
27 #include <linux/platform_device.h>
31 #define RETU_ADC_CHANNEL_HOOKDET 0x05
33 #define RETU_HEADSET_KEY KEY_PHONE
38 struct platform_device
*pdev
;
39 struct input_dev
*idev
;
40 unsigned bias_enabled
;
41 unsigned detection_enabled
;
43 struct timer_list enable_timer
;
44 struct timer_list detect_timer
;
47 static void retu_headset_set_bias(int enable
)
50 retu_set_clear_reg_bits(RETU_REG_AUDTXR
,
51 (1 << 0) | (1 << 1), 0);
53 retu_set_clear_reg_bits(RETU_REG_AUDTXR
, 1 << 3, 0);
55 retu_set_clear_reg_bits(RETU_REG_AUDTXR
, 0,
56 (1 << 0) | (1 << 1) | (1 << 3));
60 static void retu_headset_enable(struct retu_headset
*hs
)
62 mutex_lock(&hs
->mutex
);
63 if (!hs
->bias_enabled
) {
65 retu_headset_set_bias(1);
67 mutex_unlock(&hs
->mutex
);
70 static void retu_headset_disable(struct retu_headset
*hs
)
72 mutex_lock(&hs
->mutex
);
73 if (hs
->bias_enabled
) {
75 retu_headset_set_bias(0);
77 mutex_unlock(&hs
->mutex
);
80 static void retu_headset_det_enable(struct retu_headset
*hs
)
82 mutex_lock(&hs
->mutex
);
83 if (!hs
->detection_enabled
) {
84 hs
->detection_enabled
= 1;
85 retu_set_clear_reg_bits(RETU_REG_CC1
, (1 << 10) | (1 << 8), 0);
86 retu_enable_irq(RETU_INT_HOOK
);
88 mutex_unlock(&hs
->mutex
);
91 static void retu_headset_det_disable(struct retu_headset
*hs
)
95 mutex_lock(&hs
->mutex
);
96 if (hs
->detection_enabled
) {
97 hs
->detection_enabled
= 0;
98 retu_disable_irq(RETU_INT_HOOK
);
99 del_timer_sync(&hs
->enable_timer
);
100 del_timer_sync(&hs
->detect_timer
);
101 spin_lock_irqsave(&hs
->lock
, flags
);
103 input_report_key(hs
->idev
, RETU_HEADSET_KEY
, 0);
104 spin_unlock_irqrestore(&hs
->lock
, flags
);
105 retu_set_clear_reg_bits(RETU_REG_CC1
, 0, (1 << 10) | (1 << 8));
107 mutex_unlock(&hs
->mutex
);
110 static ssize_t
retu_headset_hookdet_show(struct device
*dev
,
111 struct device_attribute
*attr
,
116 val
= retu_read_adc(RETU_ADC_CHANNEL_HOOKDET
);
117 return sprintf(buf
, "%d\n", val
);
120 static DEVICE_ATTR(hookdet
, S_IRUGO
, retu_headset_hookdet_show
, NULL
);
122 static ssize_t
retu_headset_enable_show(struct device
*dev
,
123 struct device_attribute
*attr
,
126 struct retu_headset
*hs
= dev_get_drvdata(dev
);
128 return sprintf(buf
, "%u\n", hs
->bias_enabled
);
131 static ssize_t
retu_headset_enable_store(struct device
*dev
,
132 struct device_attribute
*attr
,
133 const char *buf
, size_t count
)
135 struct retu_headset
*hs
= dev_get_drvdata(dev
);
138 if (sscanf(buf
, "%u", &enable
) != 1)
141 retu_headset_enable(hs
);
143 retu_headset_disable(hs
);
147 static DEVICE_ATTR(enable
, S_IRUGO
| S_IWUSR
| S_IWGRP
,
148 retu_headset_enable_show
, retu_headset_enable_store
);
150 static ssize_t
retu_headset_enable_det_show(struct device
*dev
,
151 struct device_attribute
*attr
,
154 struct retu_headset
*hs
= dev_get_drvdata(dev
);
156 return sprintf(buf
, "%u\n", hs
->detection_enabled
);
159 static ssize_t
retu_headset_enable_det_store(struct device
*dev
,
160 struct device_attribute
*attr
,
161 const char *buf
, size_t count
)
163 struct retu_headset
*hs
= dev_get_drvdata(dev
);
166 if (sscanf(buf
, "%u", &enable
) != 1)
169 retu_headset_det_enable(hs
);
171 retu_headset_det_disable(hs
);
175 static DEVICE_ATTR(enable_det
, S_IRUGO
| S_IWUSR
| S_IWGRP
,
176 retu_headset_enable_det_show
,
177 retu_headset_enable_det_store
);
179 static void retu_headset_hook_interrupt(unsigned long arg
)
181 struct retu_headset
*hs
= (struct retu_headset
*) arg
;
184 retu_ack_irq(RETU_INT_HOOK
);
185 spin_lock_irqsave(&hs
->lock
, flags
);
187 /* Headset button was just pressed down. */
189 input_report_key(hs
->idev
, RETU_HEADSET_KEY
, 1);
191 spin_unlock_irqrestore(&hs
->lock
, flags
);
192 retu_set_clear_reg_bits(RETU_REG_CC1
, 0, (1 << 10) | (1 << 8));
193 mod_timer(&hs
->enable_timer
, jiffies
+ msecs_to_jiffies(50));
196 static void retu_headset_enable_timer(unsigned long arg
)
198 struct retu_headset
*hs
= (struct retu_headset
*) arg
;
200 retu_set_clear_reg_bits(RETU_REG_CC1
, (1 << 10) | (1 << 8), 0);
201 mod_timer(&hs
->detect_timer
, jiffies
+ msecs_to_jiffies(350));
204 static void retu_headset_detect_timer(unsigned long arg
)
206 struct retu_headset
*hs
= (struct retu_headset
*) arg
;
209 spin_lock_irqsave(&hs
->lock
, flags
);
212 input_report_key(hs
->idev
, RETU_HEADSET_KEY
, 0);
214 spin_unlock_irqrestore(&hs
->lock
, flags
);
217 static int __init
retu_headset_probe(struct platform_device
*pdev
)
219 struct retu_headset
*hs
;
222 hs
= kzalloc(sizeof(*hs
), GFP_KERNEL
);
228 hs
->idev
= input_allocate_device();
229 if (hs
->idev
== NULL
) {
233 hs
->idev
->name
= "retu-headset";
234 hs
->idev
->dev
.parent
= &pdev
->dev
;
235 set_bit(EV_KEY
, hs
->idev
->evbit
);
236 set_bit(RETU_HEADSET_KEY
, hs
->idev
->keybit
);
237 r
= input_register_device(hs
->idev
);
241 r
= device_create_file(&pdev
->dev
, &dev_attr_hookdet
);
244 r
= device_create_file(&pdev
->dev
, &dev_attr_enable
);
247 r
= device_create_file(&pdev
->dev
, &dev_attr_enable_det
);
250 platform_set_drvdata(pdev
, hs
);
252 spin_lock_init(&hs
->lock
);
253 mutex_init(&hs
->mutex
);
254 setup_timer(&hs
->enable_timer
, retu_headset_enable_timer
,
256 setup_timer(&hs
->detect_timer
, retu_headset_detect_timer
,
259 r
= retu_request_irq(RETU_INT_HOOK
, retu_headset_hook_interrupt
,
260 (unsigned long) hs
, "hookdet");
262 dev_err(&pdev
->dev
, "hookdet IRQ not available\n");
265 retu_disable_irq(RETU_INT_HOOK
);
268 device_remove_file(&pdev
->dev
, &dev_attr_enable_det
);
270 device_remove_file(&pdev
->dev
, &dev_attr_enable
);
272 device_remove_file(&pdev
->dev
, &dev_attr_hookdet
);
274 input_unregister_device(hs
->idev
);
276 input_free_device(hs
->idev
);
282 static int retu_headset_remove(struct platform_device
*pdev
)
284 struct retu_headset
*hs
= platform_get_drvdata(pdev
);
286 device_remove_file(&pdev
->dev
, &dev_attr_hookdet
);
287 device_remove_file(&pdev
->dev
, &dev_attr_enable
);
288 device_remove_file(&pdev
->dev
, &dev_attr_enable_det
);
289 retu_headset_disable(hs
);
290 retu_headset_det_disable(hs
);
291 retu_free_irq(RETU_INT_HOOK
);
292 input_unregister_device(hs
->idev
);
293 input_free_device(hs
->idev
);
297 static int retu_headset_suspend(struct platform_device
*pdev
,
300 struct retu_headset
*hs
= platform_get_drvdata(pdev
);
302 mutex_lock(&hs
->mutex
);
303 if (hs
->bias_enabled
)
304 retu_headset_set_bias(0);
305 mutex_unlock(&hs
->mutex
);
310 static int retu_headset_resume(struct platform_device
*pdev
)
312 struct retu_headset
*hs
= platform_get_drvdata(pdev
);
314 mutex_lock(&hs
->mutex
);
315 if (hs
->bias_enabled
)
316 retu_headset_set_bias(1);
317 mutex_unlock(&hs
->mutex
);
322 static struct platform_driver retu_headset_driver
= {
323 .probe
= retu_headset_probe
,
324 .remove
= retu_headset_remove
,
325 .suspend
= retu_headset_suspend
,
326 .resume
= retu_headset_resume
,
328 .name
= "retu-headset",
332 static int __init
retu_headset_init(void)
336 printk(KERN_INFO
"Retu/Vilma headset driver initializing\n");
338 r
= platform_driver_register(&retu_headset_driver
);
345 static void __exit
retu_headset_exit(void)
347 platform_driver_unregister(&retu_headset_driver
);
350 module_init(retu_headset_init
);
351 module_exit(retu_headset_exit
);
353 MODULE_DESCRIPTION("Retu/Vilma headset detection");
354 MODULE_LICENSE("GPL");
355 MODULE_AUTHOR("Juha Yrjölä");