blob: cbe585f95715ad6969181dd2d658c539eb1be9e1 [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (c) 2016 Samsung Electronics Co., Ltd.
3 * Author: Andi Shyti <andi.shyti@samsung.com>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation.
8 *
9 * SPI driven IR LED device driver
10 */
11
12#include <linux/delay.h>
13#include <linux/fs.h>
14#include <linux/module.h>
15#include <linux/mutex.h>
16#include <linux/of_gpio.h>
17#include <linux/regulator/consumer.h>
18#include <linux/spi/spi.h>
19#include <media/rc-core.h>
20
21#define IR_SPI_DRIVER_NAME "ir-spi"
22
23/* pulse value for different duty cycles */
24#define IR_SPI_PULSE_DC_50 0xff00
25#define IR_SPI_PULSE_DC_60 0xfc00
26#define IR_SPI_PULSE_DC_70 0xf800
27#define IR_SPI_PULSE_DC_75 0xf000
28#define IR_SPI_PULSE_DC_80 0xc000
29#define IR_SPI_PULSE_DC_90 0x8000
30
31#define IR_SPI_DEFAULT_FREQUENCY 38000
32#define IR_SPI_BIT_PER_WORD 8
33#define IR_SPI_MAX_BUFSIZE 4096
34
35struct ir_spi_data {
36 u32 freq;
37 u8 duty_cycle;
38 bool negated;
39
40 u16 tx_buf[IR_SPI_MAX_BUFSIZE];
41 u16 pulse;
42 u16 space;
43
44 struct rc_dev *rc;
45 struct spi_device *spi;
46 struct regulator *regulator;
47};
48
49static int ir_spi_tx(struct rc_dev *dev,
50 unsigned int *buffer, unsigned int count)
51{
52 int i;
53 int ret;
54 unsigned int len = 0;
55 struct ir_spi_data *idata = dev->priv;
56 struct spi_transfer xfer;
57
58 /* convert the pulse/space signal to raw binary signal */
59 for (i = 0; i < count; i++) {
60 unsigned int periods;
61 int j;
62 u16 val;
63
64 periods = DIV_ROUND_CLOSEST(buffer[i] * idata->freq, 1000000);
65
66 if (len + periods >= IR_SPI_MAX_BUFSIZE)
67 return -EINVAL;
68
69 /*
70 * the first value in buffer is a pulse, so that 0, 2, 4, ...
71 * contain a pulse duration. On the contrary, 1, 3, 5, ...
72 * contain a space duration.
73 */
74 val = (i % 2) ? idata->space : idata->pulse;
75 for (j = 0; j < periods; j++)
76 idata->tx_buf[len++] = val;
77 }
78
79 memset(&xfer, 0, sizeof(xfer));
80
81 xfer.speed_hz = idata->freq * 16;
82 xfer.len = len * sizeof(*idata->tx_buf);
83 xfer.tx_buf = idata->tx_buf;
84
85 ret = regulator_enable(idata->regulator);
86 if (ret)
87 return ret;
88
89 ret = spi_sync_transfer(idata->spi, &xfer, 1);
90 if (ret)
91 dev_err(&idata->spi->dev, "unable to deliver the signal\n");
92
93 regulator_disable(idata->regulator);
94
95 return ret ? ret : count;
96}
97
98static int ir_spi_set_tx_carrier(struct rc_dev *dev, u32 carrier)
99{
100 struct ir_spi_data *idata = dev->priv;
101
102 if (!carrier)
103 return -EINVAL;
104
105 idata->freq = carrier;
106
107 return 0;
108}
109
110static int ir_spi_set_duty_cycle(struct rc_dev *dev, u32 duty_cycle)
111{
112 struct ir_spi_data *idata = dev->priv;
113
114 if (duty_cycle >= 90)
115 idata->pulse = IR_SPI_PULSE_DC_90;
116 else if (duty_cycle >= 80)
117 idata->pulse = IR_SPI_PULSE_DC_80;
118 else if (duty_cycle >= 75)
119 idata->pulse = IR_SPI_PULSE_DC_75;
120 else if (duty_cycle >= 70)
121 idata->pulse = IR_SPI_PULSE_DC_70;
122 else if (duty_cycle >= 60)
123 idata->pulse = IR_SPI_PULSE_DC_60;
124 else
125 idata->pulse = IR_SPI_PULSE_DC_50;
126
127 if (idata->negated) {
128 idata->pulse = ~idata->pulse;
129 idata->space = 0xffff;
130 } else {
131 idata->space = 0;
132 }
133
134 return 0;
135}
136
137static int ir_spi_probe(struct spi_device *spi)
138{
139 int ret;
140 u8 dc;
141 struct ir_spi_data *idata;
142
143 idata = devm_kzalloc(&spi->dev, sizeof(*idata), GFP_KERNEL);
144 if (!idata)
145 return -ENOMEM;
146
147 idata->regulator = devm_regulator_get(&spi->dev, "irda_regulator");
148 if (IS_ERR(idata->regulator))
149 return PTR_ERR(idata->regulator);
150
151 idata->rc = devm_rc_allocate_device(&spi->dev, RC_DRIVER_IR_RAW_TX);
152 if (!idata->rc)
153 return -ENOMEM;
154
155 idata->rc->tx_ir = ir_spi_tx;
156 idata->rc->s_tx_carrier = ir_spi_set_tx_carrier;
157 idata->rc->s_tx_duty_cycle = ir_spi_set_duty_cycle;
158 idata->rc->device_name = "IR SPI";
159 idata->rc->driver_name = IR_SPI_DRIVER_NAME;
160 idata->rc->priv = idata;
161 idata->spi = spi;
162
163 idata->negated = of_property_read_bool(spi->dev.of_node,
164 "led-active-low");
165 ret = of_property_read_u8(spi->dev.of_node, "duty-cycle", &dc);
166 if (ret)
167 dc = 50;
168
169 /* ir_spi_set_duty_cycle cannot fail,
170 * it returns int to be compatible with the
171 * rc->s_tx_duty_cycle function
172 */
173 ir_spi_set_duty_cycle(idata->rc, dc);
174
175 idata->freq = IR_SPI_DEFAULT_FREQUENCY;
176
177 return devm_rc_register_device(&spi->dev, idata->rc);
178}
179
180static int ir_spi_remove(struct spi_device *spi)
181{
182 return 0;
183}
184
185static const struct of_device_id ir_spi_of_match[] = {
186 { .compatible = "ir-spi-led" },
187 {},
188};
189MODULE_DEVICE_TABLE(of, ir_spi_of_match);
190
191static struct spi_driver ir_spi_driver = {
192 .probe = ir_spi_probe,
193 .remove = ir_spi_remove,
194 .driver = {
195 .name = IR_SPI_DRIVER_NAME,
196 .of_match_table = ir_spi_of_match,
197 },
198};
199
200module_spi_driver(ir_spi_driver);
201
202MODULE_AUTHOR("Andi Shyti <andi.shyti@samsung.com>");
203MODULE_DESCRIPTION("SPI IR LED");
204MODULE_LICENSE("GPL v2");