|  | /* | 
|  | *  Copyright (C) 2008 Nokia Corporation | 
|  | * | 
|  | *  Based on lirc_serial.c | 
|  | * | 
|  | *  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. | 
|  | */ | 
|  | #include <linux/clk.h> | 
|  | #include <linux/module.h> | 
|  | #include <linux/platform_device.h> | 
|  | #include <linux/wait.h> | 
|  | #include <linux/pwm.h> | 
|  | #include <linux/of.h> | 
|  | #include <linux/hrtimer.h> | 
|  |  | 
|  | #include <media/rc-core.h> | 
|  |  | 
|  | #define WBUF_LEN 256 | 
|  |  | 
|  | struct ir_rx51 { | 
|  | struct rc_dev *rcdev; | 
|  | struct pwm_device *pwm; | 
|  | struct hrtimer timer; | 
|  | struct device	     *dev; | 
|  | wait_queue_head_t     wqueue; | 
|  |  | 
|  | unsigned int	freq;		/* carrier frequency */ | 
|  | unsigned int	duty_cycle;	/* carrier duty cycle */ | 
|  | int		wbuf[WBUF_LEN]; | 
|  | int		wbuf_index; | 
|  | unsigned long	device_is_open; | 
|  | }; | 
|  |  | 
|  | static inline void ir_rx51_on(struct ir_rx51 *ir_rx51) | 
|  | { | 
|  | pwm_enable(ir_rx51->pwm); | 
|  | } | 
|  |  | 
|  | static inline void ir_rx51_off(struct ir_rx51 *ir_rx51) | 
|  | { | 
|  | pwm_disable(ir_rx51->pwm); | 
|  | } | 
|  |  | 
|  | static int init_timing_params(struct ir_rx51 *ir_rx51) | 
|  | { | 
|  | struct pwm_device *pwm = ir_rx51->pwm; | 
|  | int duty, period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, ir_rx51->freq); | 
|  |  | 
|  | duty = DIV_ROUND_CLOSEST(ir_rx51->duty_cycle * period, 100); | 
|  |  | 
|  | pwm_config(pwm, duty, period); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static enum hrtimer_restart ir_rx51_timer_cb(struct hrtimer *timer) | 
|  | { | 
|  | struct ir_rx51 *ir_rx51 = container_of(timer, struct ir_rx51, timer); | 
|  | ktime_t now; | 
|  |  | 
|  | if (ir_rx51->wbuf_index < 0) { | 
|  | dev_err_ratelimited(ir_rx51->dev, | 
|  | "BUG wbuf_index has value of %i\n", | 
|  | ir_rx51->wbuf_index); | 
|  | goto end; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * If we happen to hit an odd latency spike, loop through the | 
|  | * pulses until we catch up. | 
|  | */ | 
|  | do { | 
|  | u64 ns; | 
|  |  | 
|  | if (ir_rx51->wbuf_index >= WBUF_LEN) | 
|  | goto end; | 
|  | if (ir_rx51->wbuf[ir_rx51->wbuf_index] == -1) | 
|  | goto end; | 
|  |  | 
|  | if (ir_rx51->wbuf_index % 2) | 
|  | ir_rx51_off(ir_rx51); | 
|  | else | 
|  | ir_rx51_on(ir_rx51); | 
|  |  | 
|  | ns = US_TO_NS(ir_rx51->wbuf[ir_rx51->wbuf_index]); | 
|  | hrtimer_add_expires_ns(timer, ns); | 
|  |  | 
|  | ir_rx51->wbuf_index++; | 
|  |  | 
|  | now = timer->base->get_time(); | 
|  |  | 
|  | } while (hrtimer_get_expires_tv64(timer) < now); | 
|  |  | 
|  | return HRTIMER_RESTART; | 
|  | end: | 
|  | /* Stop TX here */ | 
|  | ir_rx51_off(ir_rx51); | 
|  | ir_rx51->wbuf_index = -1; | 
|  |  | 
|  | wake_up_interruptible(&ir_rx51->wqueue); | 
|  |  | 
|  | return HRTIMER_NORESTART; | 
|  | } | 
|  |  | 
|  | static int ir_rx51_tx(struct rc_dev *dev, unsigned int *buffer, | 
|  | unsigned int count) | 
|  | { | 
|  | struct ir_rx51 *ir_rx51 = dev->priv; | 
|  |  | 
|  | if (count > WBUF_LEN) | 
|  | return -EINVAL; | 
|  |  | 
|  | memcpy(ir_rx51->wbuf, buffer, count * sizeof(unsigned int)); | 
|  |  | 
|  | /* Wait any pending transfers to finish */ | 
|  | wait_event_interruptible(ir_rx51->wqueue, ir_rx51->wbuf_index < 0); | 
|  |  | 
|  | init_timing_params(ir_rx51); | 
|  | if (count < WBUF_LEN) | 
|  | ir_rx51->wbuf[count] = -1; /* Insert termination mark */ | 
|  |  | 
|  | /* | 
|  | * REVISIT: Adjust latency requirements so the device doesn't go in too | 
|  | * deep sleep states with pm_qos_add_request(). | 
|  | */ | 
|  |  | 
|  | ir_rx51_on(ir_rx51); | 
|  | ir_rx51->wbuf_index = 1; | 
|  | hrtimer_start(&ir_rx51->timer, | 
|  | ns_to_ktime(US_TO_NS(ir_rx51->wbuf[0])), | 
|  | HRTIMER_MODE_REL); | 
|  | /* | 
|  | * Don't return back to the userspace until the transfer has | 
|  | * finished | 
|  | */ | 
|  | wait_event_interruptible(ir_rx51->wqueue, ir_rx51->wbuf_index < 0); | 
|  |  | 
|  | /* REVISIT: Remove pm_qos constraint, we can sleep again */ | 
|  |  | 
|  | return count; | 
|  | } | 
|  |  | 
|  | static int ir_rx51_open(struct rc_dev *dev) | 
|  | { | 
|  | struct ir_rx51 *ir_rx51 = dev->priv; | 
|  |  | 
|  | if (test_and_set_bit(1, &ir_rx51->device_is_open)) | 
|  | return -EBUSY; | 
|  |  | 
|  | ir_rx51->pwm = pwm_get(ir_rx51->dev, NULL); | 
|  | if (IS_ERR(ir_rx51->pwm)) { | 
|  | int res = PTR_ERR(ir_rx51->pwm); | 
|  |  | 
|  | dev_err(ir_rx51->dev, "pwm_get failed: %d\n", res); | 
|  | return res; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void ir_rx51_release(struct rc_dev *dev) | 
|  | { | 
|  | struct ir_rx51 *ir_rx51 = dev->priv; | 
|  |  | 
|  | hrtimer_cancel(&ir_rx51->timer); | 
|  | ir_rx51_off(ir_rx51); | 
|  | pwm_put(ir_rx51->pwm); | 
|  |  | 
|  | clear_bit(1, &ir_rx51->device_is_open); | 
|  | } | 
|  |  | 
|  | static struct ir_rx51 ir_rx51 = { | 
|  | .duty_cycle	= 50, | 
|  | .wbuf_index	= -1, | 
|  | }; | 
|  |  | 
|  | static int ir_rx51_set_duty_cycle(struct rc_dev *dev, u32 duty) | 
|  | { | 
|  | struct ir_rx51 *ir_rx51 = dev->priv; | 
|  |  | 
|  | ir_rx51->duty_cycle = duty; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int ir_rx51_set_tx_carrier(struct rc_dev *dev, u32 carrier) | 
|  | { | 
|  | struct ir_rx51 *ir_rx51 = dev->priv; | 
|  |  | 
|  | if (carrier > 500000 || carrier < 20000) | 
|  | return -EINVAL; | 
|  |  | 
|  | ir_rx51->freq = carrier; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #ifdef CONFIG_PM | 
|  |  | 
|  | static int ir_rx51_suspend(struct platform_device *dev, pm_message_t state) | 
|  | { | 
|  | /* | 
|  | * In case the device is still open, do not suspend. Normally | 
|  | * this should not be a problem as lircd only keeps the device | 
|  | * open only for short periods of time. We also don't want to | 
|  | * get involved with race conditions that might happen if we | 
|  | * were in a middle of a transmit. Thus, we defer any suspend | 
|  | * actions until transmit has completed. | 
|  | */ | 
|  | if (test_and_set_bit(1, &ir_rx51.device_is_open)) | 
|  | return -EAGAIN; | 
|  |  | 
|  | clear_bit(1, &ir_rx51.device_is_open); | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int ir_rx51_resume(struct platform_device *dev) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | #else | 
|  |  | 
|  | #define ir_rx51_suspend	NULL | 
|  | #define ir_rx51_resume	NULL | 
|  |  | 
|  | #endif /* CONFIG_PM */ | 
|  |  | 
|  | static int ir_rx51_probe(struct platform_device *dev) | 
|  | { | 
|  | struct pwm_device *pwm; | 
|  | struct rc_dev *rcdev; | 
|  |  | 
|  | pwm = pwm_get(&dev->dev, NULL); | 
|  | if (IS_ERR(pwm)) { | 
|  | int err = PTR_ERR(pwm); | 
|  |  | 
|  | if (err != -EPROBE_DEFER) | 
|  | dev_err(&dev->dev, "pwm_get failed: %d\n", err); | 
|  | return err; | 
|  | } | 
|  |  | 
|  | /* Use default, in case userspace does not set the carrier */ | 
|  | ir_rx51.freq = DIV_ROUND_CLOSEST(pwm_get_period(pwm), NSEC_PER_SEC); | 
|  | pwm_put(pwm); | 
|  |  | 
|  | hrtimer_init(&ir_rx51.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | 
|  | ir_rx51.timer.function = ir_rx51_timer_cb; | 
|  |  | 
|  | ir_rx51.dev = &dev->dev; | 
|  |  | 
|  | rcdev = devm_rc_allocate_device(&dev->dev, RC_DRIVER_IR_RAW_TX); | 
|  | if (!rcdev) | 
|  | return -ENOMEM; | 
|  |  | 
|  | rcdev->priv = &ir_rx51; | 
|  | rcdev->open = ir_rx51_open; | 
|  | rcdev->close = ir_rx51_release; | 
|  | rcdev->tx_ir = ir_rx51_tx; | 
|  | rcdev->s_tx_duty_cycle = ir_rx51_set_duty_cycle; | 
|  | rcdev->s_tx_carrier = ir_rx51_set_tx_carrier; | 
|  | rcdev->driver_name = KBUILD_MODNAME; | 
|  |  | 
|  | ir_rx51.rcdev = rcdev; | 
|  |  | 
|  | return devm_rc_register_device(&dev->dev, ir_rx51.rcdev); | 
|  | } | 
|  |  | 
|  | static int ir_rx51_remove(struct platform_device *dev) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct of_device_id ir_rx51_match[] = { | 
|  | { | 
|  | .compatible = "nokia,n900-ir", | 
|  | }, | 
|  | {}, | 
|  | }; | 
|  | MODULE_DEVICE_TABLE(of, ir_rx51_match); | 
|  |  | 
|  | static struct platform_driver ir_rx51_platform_driver = { | 
|  | .probe		= ir_rx51_probe, | 
|  | .remove		= ir_rx51_remove, | 
|  | .suspend	= ir_rx51_suspend, | 
|  | .resume		= ir_rx51_resume, | 
|  | .driver		= { | 
|  | .name	= KBUILD_MODNAME, | 
|  | .of_match_table = of_match_ptr(ir_rx51_match), | 
|  | }, | 
|  | }; | 
|  | module_platform_driver(ir_rx51_platform_driver); | 
|  |  | 
|  | MODULE_DESCRIPTION("IR TX driver for Nokia RX51"); | 
|  | MODULE_AUTHOR("Nokia Corporation"); | 
|  | MODULE_LICENSE("GPL"); |