1 // SPDX-License-Identifier: GPL-2.0
2 // SPI driven IR LED device driver
4 // Copyright (c) 2016 Samsung Electronics Co., Ltd.
5 // Copyright (c) Andi Shyti <andi.shyti@samsung.com>
7 #include <linux/delay.h>
9 #include <linux/module.h>
10 #include <linux/mutex.h>
11 #include <linux/of_gpio.h>
12 #include <linux/regulator/consumer.h>
13 #include <linux/spi/spi.h>
14 #include <media/rc-core.h>
16 #define IR_SPI_DRIVER_NAME "ir-spi"
18 /* pulse value for different duty cycles */
19 #define IR_SPI_PULSE_DC_50 0xff00
20 #define IR_SPI_PULSE_DC_60 0xfc00
21 #define IR_SPI_PULSE_DC_70 0xf800
22 #define IR_SPI_PULSE_DC_75 0xf000
23 #define IR_SPI_PULSE_DC_80 0xc000
24 #define IR_SPI_PULSE_DC_90 0x8000
26 #define IR_SPI_DEFAULT_FREQUENCY 38000
27 #define IR_SPI_BIT_PER_WORD 8
28 #define IR_SPI_MAX_BUFSIZE 4096
35 u16 tx_buf
[IR_SPI_MAX_BUFSIZE
];
40 struct spi_device
*spi
;
41 struct regulator
*regulator
;
44 static int ir_spi_tx(struct rc_dev
*dev
,
45 unsigned int *buffer
, unsigned int count
)
50 struct ir_spi_data
*idata
= dev
->priv
;
51 struct spi_transfer xfer
;
53 /* convert the pulse/space signal to raw binary signal */
54 for (i
= 0; i
< count
; i
++) {
59 periods
= DIV_ROUND_CLOSEST(buffer
[i
] * idata
->freq
, 1000000);
61 if (len
+ periods
>= IR_SPI_MAX_BUFSIZE
)
65 * the first value in buffer is a pulse, so that 0, 2, 4, ...
66 * contain a pulse duration. On the contrary, 1, 3, 5, ...
67 * contain a space duration.
69 val
= (i
% 2) ? idata
->space
: idata
->pulse
;
70 for (j
= 0; j
< periods
; j
++)
71 idata
->tx_buf
[len
++] = val
;
74 memset(&xfer
, 0, sizeof(xfer
));
76 xfer
.speed_hz
= idata
->freq
* 16;
77 xfer
.len
= len
* sizeof(*idata
->tx_buf
);
78 xfer
.tx_buf
= idata
->tx_buf
;
80 ret
= regulator_enable(idata
->regulator
);
84 ret
= spi_sync_transfer(idata
->spi
, &xfer
, 1);
86 dev_err(&idata
->spi
->dev
, "unable to deliver the signal\n");
88 regulator_disable(idata
->regulator
);
90 return ret
? ret
: count
;
93 static int ir_spi_set_tx_carrier(struct rc_dev
*dev
, u32 carrier
)
95 struct ir_spi_data
*idata
= dev
->priv
;
100 idata
->freq
= carrier
;
105 static int ir_spi_set_duty_cycle(struct rc_dev
*dev
, u32 duty_cycle
)
107 struct ir_spi_data
*idata
= dev
->priv
;
109 if (duty_cycle
>= 90)
110 idata
->pulse
= IR_SPI_PULSE_DC_90
;
111 else if (duty_cycle
>= 80)
112 idata
->pulse
= IR_SPI_PULSE_DC_80
;
113 else if (duty_cycle
>= 75)
114 idata
->pulse
= IR_SPI_PULSE_DC_75
;
115 else if (duty_cycle
>= 70)
116 idata
->pulse
= IR_SPI_PULSE_DC_70
;
117 else if (duty_cycle
>= 60)
118 idata
->pulse
= IR_SPI_PULSE_DC_60
;
120 idata
->pulse
= IR_SPI_PULSE_DC_50
;
122 if (idata
->negated
) {
123 idata
->pulse
= ~idata
->pulse
;
124 idata
->space
= 0xffff;
132 static int ir_spi_probe(struct spi_device
*spi
)
136 struct ir_spi_data
*idata
;
138 idata
= devm_kzalloc(&spi
->dev
, sizeof(*idata
), GFP_KERNEL
);
142 idata
->regulator
= devm_regulator_get(&spi
->dev
, "irda_regulator");
143 if (IS_ERR(idata
->regulator
))
144 return PTR_ERR(idata
->regulator
);
146 idata
->rc
= devm_rc_allocate_device(&spi
->dev
, RC_DRIVER_IR_RAW_TX
);
150 idata
->rc
->tx_ir
= ir_spi_tx
;
151 idata
->rc
->s_tx_carrier
= ir_spi_set_tx_carrier
;
152 idata
->rc
->s_tx_duty_cycle
= ir_spi_set_duty_cycle
;
153 idata
->rc
->device_name
= "IR SPI";
154 idata
->rc
->driver_name
= IR_SPI_DRIVER_NAME
;
155 idata
->rc
->priv
= idata
;
158 idata
->negated
= of_property_read_bool(spi
->dev
.of_node
,
160 ret
= of_property_read_u8(spi
->dev
.of_node
, "duty-cycle", &dc
);
164 /* ir_spi_set_duty_cycle cannot fail,
165 * it returns int to be compatible with the
166 * rc->s_tx_duty_cycle function
168 ir_spi_set_duty_cycle(idata
->rc
, dc
);
170 idata
->freq
= IR_SPI_DEFAULT_FREQUENCY
;
172 return devm_rc_register_device(&spi
->dev
, idata
->rc
);
175 static int ir_spi_remove(struct spi_device
*spi
)
180 static const struct of_device_id ir_spi_of_match
[] = {
181 { .compatible
= "ir-spi-led" },
185 static struct spi_driver ir_spi_driver
= {
186 .probe
= ir_spi_probe
,
187 .remove
= ir_spi_remove
,
189 .name
= IR_SPI_DRIVER_NAME
,
190 .of_match_table
= ir_spi_of_match
,
194 module_spi_driver(ir_spi_driver
);
196 MODULE_AUTHOR("Andi Shyti <andi.shyti@samsung.com>");
197 MODULE_DESCRIPTION("SPI IR LED");
198 MODULE_LICENSE("GPL v2");