brintos

brintos / linux-shallow public Read only

0
0
Text · 4.1 KiB · 8fc8e49 Raw
184 lines · c
1// SPDX-License-Identifier: GPL-2.02// SPI driven IR LED device driver3//4// Copyright (c) 2016 Samsung Electronics Co., Ltd.5// Copyright (c) Andi Shyti <andi@etezian.org>6 7#include <linux/bits.h>8#include <linux/device.h>9#include <linux/err.h>10#include <linux/math.h>11#include <linux/mod_devicetable.h>12#include <linux/module.h>13#include <linux/property.h>14#include <linux/regulator/consumer.h>15#include <linux/spi/spi.h>16#include <linux/string.h>17#include <linux/types.h>18 19#include <media/rc-core.h>20 21#define IR_SPI_DRIVER_NAME		"ir-spi"22 23#define IR_SPI_DEFAULT_FREQUENCY	3800024#define IR_SPI_MAX_BUFSIZE		 409625 26struct ir_spi_data {27	u32 freq;28	bool negated;29 30	u16 tx_buf[IR_SPI_MAX_BUFSIZE];31	u16 pulse;32	u16 space;33 34	struct rc_dev *rc;35	struct spi_device *spi;36	struct regulator *regulator;37};38 39static int ir_spi_tx(struct rc_dev *dev, unsigned int *buffer, unsigned int count)40{41	int i;42	int ret;43	unsigned int len = 0;44	struct ir_spi_data *idata = dev->priv;45	struct spi_transfer xfer;46 47	/* convert the pulse/space signal to raw binary signal */48	for (i = 0; i < count; i++) {49		unsigned int periods;50		int j;51		u16 val;52 53		periods = DIV_ROUND_CLOSEST(buffer[i] * idata->freq, 1000000);54 55		if (len + periods >= IR_SPI_MAX_BUFSIZE)56			return -EINVAL;57 58		/*59		 * The first value in buffer is a pulse, so that 0, 2, 4, ...60		 * contain a pulse duration. On the contrary, 1, 3, 5, ...61		 * contain a space duration.62		 */63		val = (i % 2) ? idata->space : idata->pulse;64		for (j = 0; j < periods; j++)65			idata->tx_buf[len++] = val;66	}67 68	memset(&xfer, 0, sizeof(xfer));69 70	xfer.speed_hz = idata->freq * 16;71	xfer.len = len * sizeof(*idata->tx_buf);72	xfer.tx_buf = idata->tx_buf;73 74	ret = regulator_enable(idata->regulator);75	if (ret)76		return ret;77 78	ret = spi_sync_transfer(idata->spi, &xfer, 1);79	if (ret)80		dev_err(&idata->spi->dev, "unable to deliver the signal\n");81 82	regulator_disable(idata->regulator);83 84	return ret ? ret : count;85}86 87static int ir_spi_set_tx_carrier(struct rc_dev *dev, u32 carrier)88{89	struct ir_spi_data *idata = dev->priv;90 91	if (!carrier)92		return -EINVAL;93 94	idata->freq = carrier;95 96	return 0;97}98 99static int ir_spi_set_duty_cycle(struct rc_dev *dev, u32 duty_cycle)100{101	struct ir_spi_data *idata = dev->priv;102	int bits = (duty_cycle * 15) / 100;103 104	idata->pulse = GENMASK(bits, 0);105 106	if (idata->negated) {107		idata->pulse = ~idata->pulse;108		idata->space = 0xffff;109	} else {110		idata->space = 0;111	}112 113	return 0;114}115 116static int ir_spi_probe(struct spi_device *spi)117{118	struct device *dev = &spi->dev;119	int ret;120	u8 dc;121	struct ir_spi_data *idata;122 123	idata = devm_kzalloc(dev, sizeof(*idata), GFP_KERNEL);124	if (!idata)125		return -ENOMEM;126 127	idata->regulator = devm_regulator_get(dev, "irda_regulator");128	if (IS_ERR(idata->regulator))129		return PTR_ERR(idata->regulator);130 131	idata->rc = devm_rc_allocate_device(&spi->dev, RC_DRIVER_IR_RAW_TX);132	if (!idata->rc)133		return -ENOMEM;134 135	idata->rc->tx_ir           = ir_spi_tx;136	idata->rc->s_tx_carrier    = ir_spi_set_tx_carrier;137	idata->rc->s_tx_duty_cycle = ir_spi_set_duty_cycle;138	idata->rc->device_name	   = "IR SPI";139	idata->rc->driver_name     = IR_SPI_DRIVER_NAME;140	idata->rc->priv            = idata;141	idata->spi                 = spi;142 143	idata->negated = device_property_read_bool(dev, "led-active-low");144	ret = device_property_read_u8(dev, "duty-cycle", &dc);145	if (ret)146		dc = 50;147 148	/*149	 * ir_spi_set_duty_cycle() cannot fail, it returns int150	 * to be compatible with the rc->s_tx_duty_cycle function.151	 */152	ir_spi_set_duty_cycle(idata->rc, dc);153 154	idata->freq = IR_SPI_DEFAULT_FREQUENCY;155 156	return devm_rc_register_device(dev, idata->rc);157}158 159static const struct of_device_id ir_spi_of_match[] = {160	{ .compatible = "ir-spi-led" },161	{}162};163MODULE_DEVICE_TABLE(of, ir_spi_of_match);164 165static const struct spi_device_id ir_spi_ids[] = {166	{ "ir-spi-led" },167	{}168};169MODULE_DEVICE_TABLE(spi, ir_spi_ids);170 171static struct spi_driver ir_spi_driver = {172	.probe = ir_spi_probe,173	.id_table = ir_spi_ids,174	.driver = {175		.name = IR_SPI_DRIVER_NAME,176		.of_match_table = ir_spi_of_match,177	},178};179module_spi_driver(ir_spi_driver);180 181MODULE_AUTHOR("Andi Shyti <andi@etezian.org>");182MODULE_DESCRIPTION("SPI IR LED");183MODULE_LICENSE("GPL v2");184