| /* | 
 |  *  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"); |