18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  Copyright (C) 2008 Nokia Corporation
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *  Based on lirc_serial.c
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/clk.h>
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
108c2ecf20Sopenharmony_ci#include <linux/wait.h>
118c2ecf20Sopenharmony_ci#include <linux/pwm.h>
128c2ecf20Sopenharmony_ci#include <linux/of.h>
138c2ecf20Sopenharmony_ci#include <linux/hrtimer.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <media/rc-core.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#define WBUF_LEN 256
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_cistruct ir_rx51 {
208c2ecf20Sopenharmony_ci	struct rc_dev *rcdev;
218c2ecf20Sopenharmony_ci	struct pwm_device *pwm;
228c2ecf20Sopenharmony_ci	struct hrtimer timer;
238c2ecf20Sopenharmony_ci	struct device	     *dev;
248c2ecf20Sopenharmony_ci	wait_queue_head_t     wqueue;
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ci	unsigned int	freq;		/* carrier frequency */
278c2ecf20Sopenharmony_ci	unsigned int	duty_cycle;	/* carrier duty cycle */
288c2ecf20Sopenharmony_ci	int		wbuf[WBUF_LEN];
298c2ecf20Sopenharmony_ci	int		wbuf_index;
308c2ecf20Sopenharmony_ci	unsigned long	device_is_open;
318c2ecf20Sopenharmony_ci};
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_cistatic inline void ir_rx51_on(struct ir_rx51 *ir_rx51)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	pwm_enable(ir_rx51->pwm);
368c2ecf20Sopenharmony_ci}
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic inline void ir_rx51_off(struct ir_rx51 *ir_rx51)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	pwm_disable(ir_rx51->pwm);
418c2ecf20Sopenharmony_ci}
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_cistatic int init_timing_params(struct ir_rx51 *ir_rx51)
448c2ecf20Sopenharmony_ci{
458c2ecf20Sopenharmony_ci	struct pwm_device *pwm = ir_rx51->pwm;
468c2ecf20Sopenharmony_ci	int duty, period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, ir_rx51->freq);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	duty = DIV_ROUND_CLOSEST(ir_rx51->duty_cycle * period, 100);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci	pwm_config(pwm, duty, period);
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	return 0;
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic enum hrtimer_restart ir_rx51_timer_cb(struct hrtimer *timer)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	struct ir_rx51 *ir_rx51 = container_of(timer, struct ir_rx51, timer);
588c2ecf20Sopenharmony_ci	ktime_t now;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	if (ir_rx51->wbuf_index < 0) {
618c2ecf20Sopenharmony_ci		dev_err_ratelimited(ir_rx51->dev,
628c2ecf20Sopenharmony_ci				    "BUG wbuf_index has value of %i\n",
638c2ecf20Sopenharmony_ci				    ir_rx51->wbuf_index);
648c2ecf20Sopenharmony_ci		goto end;
658c2ecf20Sopenharmony_ci	}
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	/*
688c2ecf20Sopenharmony_ci	 * If we happen to hit an odd latency spike, loop through the
698c2ecf20Sopenharmony_ci	 * pulses until we catch up.
708c2ecf20Sopenharmony_ci	 */
718c2ecf20Sopenharmony_ci	do {
728c2ecf20Sopenharmony_ci		u64 ns;
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_ci		if (ir_rx51->wbuf_index >= WBUF_LEN)
758c2ecf20Sopenharmony_ci			goto end;
768c2ecf20Sopenharmony_ci		if (ir_rx51->wbuf[ir_rx51->wbuf_index] == -1)
778c2ecf20Sopenharmony_ci			goto end;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci		if (ir_rx51->wbuf_index % 2)
808c2ecf20Sopenharmony_ci			ir_rx51_off(ir_rx51);
818c2ecf20Sopenharmony_ci		else
828c2ecf20Sopenharmony_ci			ir_rx51_on(ir_rx51);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci		ns = US_TO_NS(ir_rx51->wbuf[ir_rx51->wbuf_index]);
858c2ecf20Sopenharmony_ci		hrtimer_add_expires_ns(timer, ns);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci		ir_rx51->wbuf_index++;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci		now = timer->base->get_time();
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	} while (hrtimer_get_expires_tv64(timer) < now);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	return HRTIMER_RESTART;
948c2ecf20Sopenharmony_ciend:
958c2ecf20Sopenharmony_ci	/* Stop TX here */
968c2ecf20Sopenharmony_ci	ir_rx51_off(ir_rx51);
978c2ecf20Sopenharmony_ci	ir_rx51->wbuf_index = -1;
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	wake_up_interruptible(&ir_rx51->wqueue);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	return HRTIMER_NORESTART;
1028c2ecf20Sopenharmony_ci}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_cistatic int ir_rx51_tx(struct rc_dev *dev, unsigned int *buffer,
1058c2ecf20Sopenharmony_ci		      unsigned int count)
1068c2ecf20Sopenharmony_ci{
1078c2ecf20Sopenharmony_ci	struct ir_rx51 *ir_rx51 = dev->priv;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	if (count > WBUF_LEN)
1108c2ecf20Sopenharmony_ci		return -EINVAL;
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	memcpy(ir_rx51->wbuf, buffer, count * sizeof(unsigned int));
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	/* Wait any pending transfers to finish */
1158c2ecf20Sopenharmony_ci	wait_event_interruptible(ir_rx51->wqueue, ir_rx51->wbuf_index < 0);
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	init_timing_params(ir_rx51);
1188c2ecf20Sopenharmony_ci	if (count < WBUF_LEN)
1198c2ecf20Sopenharmony_ci		ir_rx51->wbuf[count] = -1; /* Insert termination mark */
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ci	/*
1228c2ecf20Sopenharmony_ci	 * REVISIT: Adjust latency requirements so the device doesn't go in too
1238c2ecf20Sopenharmony_ci	 * deep sleep states with pm_qos_add_request().
1248c2ecf20Sopenharmony_ci	 */
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	ir_rx51_on(ir_rx51);
1278c2ecf20Sopenharmony_ci	ir_rx51->wbuf_index = 1;
1288c2ecf20Sopenharmony_ci	hrtimer_start(&ir_rx51->timer,
1298c2ecf20Sopenharmony_ci		      ns_to_ktime(US_TO_NS(ir_rx51->wbuf[0])),
1308c2ecf20Sopenharmony_ci		      HRTIMER_MODE_REL);
1318c2ecf20Sopenharmony_ci	/*
1328c2ecf20Sopenharmony_ci	 * Don't return back to the userspace until the transfer has
1338c2ecf20Sopenharmony_ci	 * finished
1348c2ecf20Sopenharmony_ci	 */
1358c2ecf20Sopenharmony_ci	wait_event_interruptible(ir_rx51->wqueue, ir_rx51->wbuf_index < 0);
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	/* REVISIT: Remove pm_qos constraint, we can sleep again */
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	return count;
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic int ir_rx51_open(struct rc_dev *dev)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	struct ir_rx51 *ir_rx51 = dev->priv;
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	if (test_and_set_bit(1, &ir_rx51->device_is_open))
1478c2ecf20Sopenharmony_ci		return -EBUSY;
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	ir_rx51->pwm = pwm_get(ir_rx51->dev, NULL);
1508c2ecf20Sopenharmony_ci	if (IS_ERR(ir_rx51->pwm)) {
1518c2ecf20Sopenharmony_ci		int res = PTR_ERR(ir_rx51->pwm);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci		dev_err(ir_rx51->dev, "pwm_get failed: %d\n", res);
1548c2ecf20Sopenharmony_ci		return res;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return 0;
1588c2ecf20Sopenharmony_ci}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_cistatic void ir_rx51_release(struct rc_dev *dev)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	struct ir_rx51 *ir_rx51 = dev->priv;
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci	hrtimer_cancel(&ir_rx51->timer);
1658c2ecf20Sopenharmony_ci	ir_rx51_off(ir_rx51);
1668c2ecf20Sopenharmony_ci	pwm_put(ir_rx51->pwm);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	clear_bit(1, &ir_rx51->device_is_open);
1698c2ecf20Sopenharmony_ci}
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_cistatic struct ir_rx51 ir_rx51 = {
1728c2ecf20Sopenharmony_ci	.duty_cycle	= 50,
1738c2ecf20Sopenharmony_ci	.wbuf_index	= -1,
1748c2ecf20Sopenharmony_ci};
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic int ir_rx51_set_duty_cycle(struct rc_dev *dev, u32 duty)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	struct ir_rx51 *ir_rx51 = dev->priv;
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	ir_rx51->duty_cycle = duty;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	return 0;
1838c2ecf20Sopenharmony_ci}
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_cistatic int ir_rx51_set_tx_carrier(struct rc_dev *dev, u32 carrier)
1868c2ecf20Sopenharmony_ci{
1878c2ecf20Sopenharmony_ci	struct ir_rx51 *ir_rx51 = dev->priv;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	if (carrier > 500000 || carrier < 20000)
1908c2ecf20Sopenharmony_ci		return -EINVAL;
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci	ir_rx51->freq = carrier;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	return 0;
1958c2ecf20Sopenharmony_ci}
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci#ifdef CONFIG_PM
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic int ir_rx51_suspend(struct platform_device *dev, pm_message_t state)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	/*
2028c2ecf20Sopenharmony_ci	 * In case the device is still open, do not suspend. Normally
2038c2ecf20Sopenharmony_ci	 * this should not be a problem as lircd only keeps the device
2048c2ecf20Sopenharmony_ci	 * open only for short periods of time. We also don't want to
2058c2ecf20Sopenharmony_ci	 * get involved with race conditions that might happen if we
2068c2ecf20Sopenharmony_ci	 * were in a middle of a transmit. Thus, we defer any suspend
2078c2ecf20Sopenharmony_ci	 * actions until transmit has completed.
2088c2ecf20Sopenharmony_ci	 */
2098c2ecf20Sopenharmony_ci	if (test_and_set_bit(1, &ir_rx51.device_is_open))
2108c2ecf20Sopenharmony_ci		return -EAGAIN;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	clear_bit(1, &ir_rx51.device_is_open);
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	return 0;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_cistatic int ir_rx51_resume(struct platform_device *dev)
2188c2ecf20Sopenharmony_ci{
2198c2ecf20Sopenharmony_ci	return 0;
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_ci#else
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci#define ir_rx51_suspend	NULL
2258c2ecf20Sopenharmony_ci#define ir_rx51_resume	NULL
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci#endif /* CONFIG_PM */
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_cistatic int ir_rx51_probe(struct platform_device *dev)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	struct pwm_device *pwm;
2328c2ecf20Sopenharmony_ci	struct rc_dev *rcdev;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	pwm = pwm_get(&dev->dev, NULL);
2358c2ecf20Sopenharmony_ci	if (IS_ERR(pwm)) {
2368c2ecf20Sopenharmony_ci		int err = PTR_ERR(pwm);
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci		if (err != -EPROBE_DEFER)
2398c2ecf20Sopenharmony_ci			dev_err(&dev->dev, "pwm_get failed: %d\n", err);
2408c2ecf20Sopenharmony_ci		return err;
2418c2ecf20Sopenharmony_ci	}
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	/* Use default, in case userspace does not set the carrier */
2448c2ecf20Sopenharmony_ci	ir_rx51.freq = DIV_ROUND_CLOSEST_ULL(pwm_get_period(pwm), NSEC_PER_SEC);
2458c2ecf20Sopenharmony_ci	pwm_put(pwm);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	hrtimer_init(&ir_rx51.timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
2488c2ecf20Sopenharmony_ci	ir_rx51.timer.function = ir_rx51_timer_cb;
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	ir_rx51.dev = &dev->dev;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	rcdev = devm_rc_allocate_device(&dev->dev, RC_DRIVER_IR_RAW_TX);
2538c2ecf20Sopenharmony_ci	if (!rcdev)
2548c2ecf20Sopenharmony_ci		return -ENOMEM;
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	rcdev->priv = &ir_rx51;
2578c2ecf20Sopenharmony_ci	rcdev->open = ir_rx51_open;
2588c2ecf20Sopenharmony_ci	rcdev->close = ir_rx51_release;
2598c2ecf20Sopenharmony_ci	rcdev->tx_ir = ir_rx51_tx;
2608c2ecf20Sopenharmony_ci	rcdev->s_tx_duty_cycle = ir_rx51_set_duty_cycle;
2618c2ecf20Sopenharmony_ci	rcdev->s_tx_carrier = ir_rx51_set_tx_carrier;
2628c2ecf20Sopenharmony_ci	rcdev->driver_name = KBUILD_MODNAME;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	ir_rx51.rcdev = rcdev;
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	return devm_rc_register_device(&dev->dev, ir_rx51.rcdev);
2678c2ecf20Sopenharmony_ci}
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic int ir_rx51_remove(struct platform_device *dev)
2708c2ecf20Sopenharmony_ci{
2718c2ecf20Sopenharmony_ci	return 0;
2728c2ecf20Sopenharmony_ci}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_cistatic const struct of_device_id ir_rx51_match[] = {
2758c2ecf20Sopenharmony_ci	{
2768c2ecf20Sopenharmony_ci		.compatible = "nokia,n900-ir",
2778c2ecf20Sopenharmony_ci	},
2788c2ecf20Sopenharmony_ci	{},
2798c2ecf20Sopenharmony_ci};
2808c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, ir_rx51_match);
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_cistatic struct platform_driver ir_rx51_platform_driver = {
2838c2ecf20Sopenharmony_ci	.probe		= ir_rx51_probe,
2848c2ecf20Sopenharmony_ci	.remove		= ir_rx51_remove,
2858c2ecf20Sopenharmony_ci	.suspend	= ir_rx51_suspend,
2868c2ecf20Sopenharmony_ci	.resume		= ir_rx51_resume,
2878c2ecf20Sopenharmony_ci	.driver		= {
2888c2ecf20Sopenharmony_ci		.name	= KBUILD_MODNAME,
2898c2ecf20Sopenharmony_ci		.of_match_table = of_match_ptr(ir_rx51_match),
2908c2ecf20Sopenharmony_ci	},
2918c2ecf20Sopenharmony_ci};
2928c2ecf20Sopenharmony_cimodule_platform_driver(ir_rx51_platform_driver);
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("IR TX driver for Nokia RX51");
2958c2ecf20Sopenharmony_ciMODULE_AUTHOR("Nokia Corporation");
2968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
297