Skip to contentSkip to conversation

Generic Raspberry Pi IR transmitters

PATCH In review Sean Young 7 participants 18 messages Jul 7, 2017 – Aug 3, 2017 4 messages not in the archive

Subsystems (28)
lore
Share & tools
lore

Sean Young

author maintainer
#0 lore raw

v2 cover Generic Raspberry Pi IR transmitters

These drivers are generic, but they've been tested on Raspberry Pi.

Note that the gpio-ir-recv is a separate driver, so you will end up with two /dev/lircN devices if you want a receiver and a transmitter, whilst with the lirc_rpi driver you have one /dev/lircN device.

Also note that it might be possible for the Nokia N900 to use the pwm-ir-tx driver, making ir-rx51 obsolete. The pwm-ir-tx driver is shorter and simpler.

Changes since v2:
- Removed pointless suspend handling from pwm-ir-tx
- Split out device tree bindings to separate commits

Sean Young (6):
[media] rc-core: rename input_name to device_name
[media] rc: mce kbd decoder not needed for IR TX drivers [media] rc: gpio-ir-tx: add new driver
[media] rc: pwm-ir-tx: add new driver
[media] dt-bindings: gpio-ir-tx: add support for GPIO IR Transmitter [media] dt-bindings: pwm-ir-tx: Add support for PWM IR Transmitter

 .../devicetree/bindings/leds/irled/gpio-ir-tx.txt  |  11 ++
 .../devicetree/bindings/leds/irled/pwm-ir-tx.txt   |  13 ++
 MAINTAINERS                                        |  12 ++
 drivers/hid/hid-picolcd_cir.c                      |   2 +-
 drivers/media/cec/cec-core.c                       |   4 +-
 drivers/media/common/siano/smsir.c                 |   4 +-
 drivers/media/i2c/ir-kbd-i2c.c                     |   2 +-
 drivers/media/pci/bt8xx/bttv-input.c               |   2 +-
 drivers/media/pci/cx23885/cx23885-input.c          |   2 +-
 drivers/media/pci/cx88/cx88-input.c                |   2 +-
 drivers/media/pci/dm1105/dm1105.c                  |   2 +-
 drivers/media/pci/mantis/mantis_common.h           |   2 +-
 drivers/media/pci/mantis/mantis_input.c            |   4 +-
 drivers/media/pci/saa7134/saa7134-input.c          |   2 +-
 drivers/media/pci/smipcie/smipcie-ir.c             |   4 +-
 drivers/media/pci/smipcie/smipcie.h                |   2 +-
 drivers/media/pci/ttpci/budget-ci.c                |   2 +-
 drivers/media/rc/Kconfig                           |  23 +++
 drivers/media/rc/Makefile                          |   2 +
 drivers/media/rc/ati_remote.c                      |   2 +-
 drivers/media/rc/ene_ir.c                          |   4 +-
 drivers/media/rc/fintek-cir.c                      |   2 +-
 drivers/media/rc/gpio-ir-recv.c                    |   2 +-
 drivers/media/rc/gpio-ir-tx.c                      | 189 +++++++++++++++++++++
 drivers/media/rc/igorplugusb.c                     |   2 +-
 drivers/media/rc/iguanair.c                        |   2 +-
 drivers/media/rc/img-ir/img-ir-hw.c                |   2 +-
 drivers/media/rc/img-ir/img-ir-raw.c               |   2 +-
 drivers/media/rc/imon.c                            |   2 +-
 drivers/media/rc/ir-hix5hd2.c                      |   2 +-
 drivers/media/rc/ir-mce_kbd-decoder.c              |   3 +
 drivers/media/rc/ir-spi.c                          |   1 +
 drivers/media/rc/ite-cir.c                         |   2 +-
 drivers/media/rc/mceusb.c                          |   2 +-
 drivers/media/rc/meson-ir.c                        |   2 +-
 drivers/media/rc/mtk-cir.c                         |   2 +-
 drivers/media/rc/nuvoton-cir.c                     |   2 +-
 drivers/media/rc/pwm-ir-tx.c                       | 138 +++++++++++++++
 drivers/media/rc/rc-loopback.c                     |   2 +-
 drivers/media/rc/rc-main.c                         |   8 +-
 drivers/media/rc/redrat3.c                         |   2 +-
 drivers/media/rc/serial_ir.c                       |  10 +-
 drivers/media/rc/sir_ir.c                          |   2 +-
 drivers/media/rc/st_rc.c                           |   2 +-
 drivers/media/rc/streamzap.c                       |   2 +-
 drivers/media/rc/sunxi-cir.c                       |   2 +-
 drivers/media/rc/ttusbir.c                         |   2 +-
 drivers/media/rc/winbond-cir.c                     |   2 +-
 drivers/media/usb/au0828/au0828-input.c            |   2 +-
 drivers/media/usb/dvb-usb-v2/dvb_usb_core.c        |   5 +-
 drivers/media/usb/dvb-usb/dvb-usb-remote.c         |   2 +-
 drivers/media/usb/em28xx/em28xx-input.c            |   2 +-
 drivers/media/usb/tm6000/tm6000-input.c            |   2 +-
 include/media/cec.h                                |   2 +-
 include/media/rc-core.h                            |   6 +-
 55 files changed, 453 insertions(+), 62 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/leds/irled/gpio-ir-tx.txt
 create mode 100644 Documentation/devicetree/bindings/leds/irled/pwm-ir-tx.txt
 create mode 100644 drivers/media/rc/gpio-ir-tx.c
 create mode 100644 drivers/media/rc/pwm-ir-tx.c
Signature · 1 line
2.9.4
author maintainer
#1 lore raw

PATCH v2 1/6 rc-core: rename input_name to device_name

When an ir-spi is registered, you get this message.

rc rc0: Unspecified device as /devices/platform/soc/3f215080.spi/spi_master/spi32766/spi32766.128/rc/rc0

"Unspecified device" refers to input_name, which makes no sense for IR TX only devices. So, rename to device_name.

Also make driver_name const char* so that no casts are needed anywhere.

Now ir-spi reports:

rc rc0: IR SPI as /devices/platform/soc/3f215080.spi/spi_master/spi32766/spi32766.128/rc/rc0

---
 drivers/hid/hid-picolcd_cir.c               |  2 +-
 drivers/media/cec/cec-core.c                |  4 ++--
 drivers/media/common/siano/smsir.c          |  4 ++--
 drivers/media/i2c/ir-kbd-i2c.c              |  2 +-
 drivers/media/pci/bt8xx/bttv-input.c        |  2 +-
 drivers/media/pci/cx23885/cx23885-input.c   |  2 +-
 drivers/media/pci/cx88/cx88-input.c         |  2 +-
 drivers/media/pci/dm1105/dm1105.c           |  2 +-
 drivers/media/pci/mantis/mantis_common.h    |  2 +-
 drivers/media/pci/mantis/mantis_input.c     |  4 ++--
 drivers/media/pci/saa7134/saa7134-input.c   |  2 +-
 drivers/media/pci/smipcie/smipcie-ir.c      |  4 ++--
 drivers/media/pci/smipcie/smipcie.h         |  2 +-
 drivers/media/pci/ttpci/budget-ci.c         |  2 +-
 drivers/media/rc/ati_remote.c               |  2 +-
 drivers/media/rc/ene_ir.c                   |  4 ++--
 drivers/media/rc/fintek-cir.c               |  2 +-
 drivers/media/rc/gpio-ir-recv.c             |  2 +-
 drivers/media/rc/igorplugusb.c              |  2 +-
 drivers/media/rc/iguanair.c                 |  2 +-
 drivers/media/rc/img-ir/img-ir-hw.c         |  2 +-
 drivers/media/rc/img-ir/img-ir-raw.c        |  2 +-
 drivers/media/rc/imon.c                     |  2 +-
 drivers/media/rc/ir-hix5hd2.c               |  2 +-
 drivers/media/rc/ir-spi.c                   |  1 +
 drivers/media/rc/ite-cir.c                  |  2 +-
 drivers/media/rc/mceusb.c                   |  2 +-
 drivers/media/rc/meson-ir.c                 |  2 +-
 drivers/media/rc/mtk-cir.c                  |  2 +-
 drivers/media/rc/nuvoton-cir.c              |  2 +-
 drivers/media/rc/rc-loopback.c              |  2 +-
 drivers/media/rc/rc-main.c                  |  8 ++++----
 drivers/media/rc/redrat3.c                  |  2 +-
 drivers/media/rc/serial_ir.c                | 10 +++++-----
 drivers/media/rc/sir_ir.c                   |  2 +-
 drivers/media/rc/st_rc.c                    |  2 +-
 drivers/media/rc/streamzap.c                |  2 +-
 drivers/media/rc/sunxi-cir.c                |  2 +-
 drivers/media/rc/ttusbir.c                  |  2 +-
 drivers/media/rc/winbond-cir.c              |  2 +-
 drivers/media/usb/au0828/au0828-input.c     |  2 +-
 drivers/media/usb/dvb-usb-v2/dvb_usb_core.c |  5 ++---
 drivers/media/usb/dvb-usb/dvb-usb-remote.c  |  2 +-
 drivers/media/usb/em28xx/em28xx-input.c     |  2 +-
 drivers/media/usb/tm6000/tm6000-input.c     |  2 +-
 include/media/cec.h                         |  2 +-
 include/media/rc-core.h                     |  6 +++---
 47 files changed, 62 insertions(+), 62 deletions(-)
drivers/hid/hid-picolcd_cir.c +1 -1
index 8ffbb6f..b3b2233 100644
@@ -116,7 +116,7 @@ int picolcd_init_cir(struct picolcd_data *data, struct hid_report *report)
 	rdev->allowed_protocols = RC_BIT_ALL_IR_DECODER;
 	rdev->open             = picolcd_cir_open;
 	rdev->close            = picolcd_cir_close;
-	rdev->input_name       = data->hdev->name;
+	rdev->device_name      = data->hdev->name;
 	rdev->input_phys       = data->hdev->phys;
 	rdev->input_id.bustype = data->hdev->bus;
 	rdev->input_id.vendor  = data->hdev->vendor;
drivers/media/cec/cec-core.c +2 -2
index b516d59..1d5087f 100644
@@ -263,12 +263,12 @@ struct cec_adapter *cec_allocate_adapter(const struct cec_adap_ops *ops,
 		return ERR_PTR(-ENOMEM);
 	}
 
-	snprintf(adap->input_name, sizeof(adap->input_name),
+	snprintf(adap->device_name, sizeof(adap->device_name),
 		 "RC for %s", name);
 	snprintf(adap->input_phys, sizeof(adap->input_phys),
 		 "%s/input0", name);
 
-	adap->rc->input_name = adap->input_name;
+	adap->rc->device_name = adap->device_name;
 	adap->rc->input_phys = adap->input_phys;
 	adap->rc->input_id.bustype = BUS_CEC;
 	adap->rc->input_id.vendor = 0;
drivers/media/common/siano/smsir.c +2 -2
index 7c898b0..941c342 100644
@@ -73,7 +73,7 @@ int sms_ir_init(struct smscore_device_t *coredev)
 	strlcpy(coredev->ir.phys, coredev->devpath, sizeof(coredev->ir.phys));
 	strlcat(coredev->ir.phys, "/ir0", sizeof(coredev->ir.phys));
 
-	dev->input_name = coredev->ir.name;
+	dev->device_name = coredev->ir.name;
 	dev->input_phys = coredev->ir.phys;
 	dev->dev.parent = coredev->device;
 
@@ -91,7 +91,7 @@ int sms_ir_init(struct smscore_device_t *coredev)
 	dev->driver_name = MODULE_NAME;
 
 	pr_debug("Input device (IR) %s is set for key events\n",
-		 dev->input_name);
+		 dev->device_name);
 
 	err = rc_register_device(dev);
 	if (err < 0) {
drivers/media/i2c/ir-kbd-i2c.c +1 -1
index cee7fd9..af909bf 100644
@@ -452,7 +452,7 @@ static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
 	 */
 	rc->input_id.bustype = BUS_I2C;
 	rc->input_phys       = ir->phys;
-	rc->input_name	     = ir->name;
+	rc->device_name	     = ir->name;
 
 	/*
 	 * Initialize the other fields of rc_dev
drivers/media/pci/bt8xx/bttv-input.c +1 -1
index 2fd07a8..bb8eda5 100644
@@ -535,7 +535,7 @@ int bttv_input_init(struct bttv *btv)
 	snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0",
 		 pci_name(btv->c.pci));
 
-	rc->input_name = ir->name;
+	rc->device_name = ir->name;
 	rc->input_phys = ir->phys;
 	rc->input_id.bustype = BUS_PCI;
 	rc->input_id.version = 1;
drivers/media/pci/cx23885/cx23885-input.c +1 -1
index 4367cb3..c9b8e3f 100644
@@ -351,7 +351,7 @@ int cx23885_input_init(struct cx23885_dev *dev)
 	}
 
 	kernel_ir->rc = rc;
-	rc->input_name = kernel_ir->name;
+	rc->device_name = kernel_ir->name;
 	rc->input_phys = kernel_ir->phys;
 	rc->input_id.bustype = BUS_PCI;
 	rc->input_id.version = 1;
drivers/media/pci/cx88/cx88-input.c +1 -1
index 01f2e47..a5dbee7 100644
@@ -464,7 +464,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
 	snprintf(ir->name, sizeof(ir->name), "cx88 IR (%s)", core->board.name);
 	snprintf(ir->phys, sizeof(ir->phys), "pci-%s/ir0", pci_name(pci));
 
-	dev->input_name = ir->name;
+	dev->device_name = ir->name;
 	dev->input_phys = ir->phys;
 	dev->input_id.bustype = BUS_PCI;
 	dev->input_id.version = 1;
drivers/media/pci/dm1105/dm1105.c +1 -1
index 1d41934..e69ec33 100644
@@ -748,7 +748,7 @@ static int dm1105_ir_init(struct dm1105_dev *dm1105)
 
 	dev->driver_name = MODULE_NAME;
 	dev->map_name = RC_MAP_DM1105_NEC;
-	dev->input_name = "DVB on-card IR receiver";
+	dev->device_name = "DVB on-card IR receiver";
 	dev->input_phys = dm1105->ir.input_phys;
 	dev->input_id.bustype = BUS_PCI;
 	dev->input_id.version = 1;
drivers/media/pci/mantis/mantis_common.h +1 -1
index d48778a..a664c31 100644
@@ -176,7 +176,7 @@ struct mantis_pci {
 	struct work_struct	uart_work;
 
 	struct rc_dev		*rc;
-	char			input_name[80];
+	char			device_name[80];
 	char			input_phys[80];
 	char			*rc_map_name;
 };
drivers/media/pci/mantis/mantis_input.c +2 -2
index 50d10cb..001b7f8 100644
@@ -46,12 +46,12 @@ int mantis_input_init(struct mantis_pci *mantis)
 		goto out;
 	}
 
-	snprintf(mantis->input_name, sizeof(mantis->input_name),
+	snprintf(mantis->device_name, sizeof(mantis->device_name),
 		 "Mantis %s IR receiver", mantis->hwconfig->model_name);
 	snprintf(mantis->input_phys, sizeof(mantis->input_phys),
 		 "pci-%s/ir0", pci_name(mantis->pdev));
 
-	dev->input_name         = mantis->input_name;
+	dev->device_name        = mantis->device_name;
 	dev->input_phys         = mantis->input_phys;
 	dev->input_id.bustype   = BUS_PCI;
 	dev->input_id.vendor    = mantis->vendor_id;
drivers/media/pci/saa7134/saa7134-input.c +1 -1
index 78849c1..ba1fc77 100644
@@ -870,7 +870,7 @@ int saa7134_input_init1(struct saa7134_dev *dev)
 	if (raw_decode)
 		rc->driver_type = RC_DRIVER_IR_RAW;
 
-	rc->input_name = ir->name;
+	rc->device_name = ir->name;
 	rc->input_phys = ir->phys;
 	rc->input_id.bustype = BUS_PCI;
 	rc->input_id.version = 1;
drivers/media/pci/smipcie/smipcie-ir.c +2 -2
index d2730c3..fc33757 100644
@@ -188,14 +188,14 @@ int smi_ir_init(struct smi_dev *dev)
 		return -ENOMEM;
 
 	/* init input device */
-	snprintf(ir->input_name, sizeof(ir->input_name), "IR (%s)",
+	snprintf(ir->device_name, sizeof(ir->device_name), "IR (%s)",
 		 dev->info->name);
 	snprintf(ir->input_phys, sizeof(ir->input_phys), "pci-%s/ir0",
 		 pci_name(dev->pci_dev));
 
 	rc_dev->driver_name = "SMI_PCIe";
 	rc_dev->input_phys = ir->input_phys;
-	rc_dev->input_name = ir->input_name;
+	rc_dev->device_name = ir->device_name;
 	rc_dev->input_id.bustype = BUS_PCI;
 	rc_dev->input_id.version = 1;
 	rc_dev->input_id.vendor = dev->pci_dev->subsystem_vendor;
drivers/media/pci/smipcie/smipcie.h +1 -1
index 611e4f0..c8368c7 100644
@@ -240,7 +240,7 @@ struct smi_rc {
 	struct smi_dev *dev;
 	struct rc_dev *rc_dev;
 	char input_phys[64];
-	char input_name[64];
+	char device_name[64];
 	struct work_struct work;
 	u8 irData[256];
 
drivers/media/pci/ttpci/budget-ci.c +1 -1
index 11b9227..cd4e63b 100644
@@ -186,7 +186,7 @@ static int msp430_ir_init(struct budget_ci *budget_ci)
 		 "pci-%s/ir0", pci_name(saa->pci));
 
 	dev->driver_name = MODULE_NAME;
-	dev->input_name = budget_ci->ir.name;
+	dev->device_name = budget_ci->ir.name;
 	dev->input_phys = budget_ci->ir.phys;
 	dev->input_id.bustype = BUS_PCI;
 	dev->input_id.version = 1;
drivers/media/rc/ati_remote.c +1 -1
index a4c6ad4..a7f7600 100644
@@ -766,7 +766,7 @@ static void ati_remote_rc_init(struct ati_remote *ati_remote)
 	rdev->open = ati_remote_rc_open;
 	rdev->close = ati_remote_rc_close;
 
-	rdev->input_name = ati_remote->rc_name;
+	rdev->device_name = ati_remote->rc_name;
 	rdev->input_phys = ati_remote->rc_phys;
 
 	usb_to_input_id(ati_remote->udev, &rdev->input_id);
drivers/media/rc/ene_ir.c +2 -2
index 60da963..41f6b1c 100644
@@ -1060,7 +1060,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
 	rdev->s_idle = ene_set_idle;
 	rdev->driver_name = ENE_DRIVER_NAME;
 	rdev->map_name = RC_MAP_RC6_MCE;
-	rdev->input_name = "ENE eHome Infrared Remote Receiver";
+	rdev->device_name = "ENE eHome Infrared Remote Receiver";
 
 	if (dev->hw_learning_and_tx_capable) {
 		rdev->s_learning_mode = ene_set_learning_mode;
@@ -1070,7 +1070,7 @@ static int ene_probe(struct pnp_dev *pnp_dev, const struct pnp_device_id *id)
 		rdev->s_tx_carrier = ene_set_tx_carrier;
 		rdev->s_tx_duty_cycle = ene_set_tx_duty_cycle;
 		rdev->s_carrier_report = ene_set_carrier_report;
-		rdev->input_name = "ENE eHome Infrared Remote Transceiver";
+		rdev->device_name = "ENE eHome Infrared Remote Transceiver";
 	}
 
 	dev->rdev = rdev;
drivers/media/rc/fintek-cir.c +1 -1
index 0d35627..57155e4 100644
@@ -532,7 +532,7 @@ static int fintek_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id
 	rdev->allowed_protocols = RC_BIT_ALL_IR_DECODER;
 	rdev->open = fintek_open;
 	rdev->close = fintek_close;
-	rdev->input_name = FINTEK_DESCRIPTION;
+	rdev->device_name = FINTEK_DESCRIPTION;
 	rdev->input_phys = "fintek/cir0";
 	rdev->input_id.bustype = BUS_HOST;
 	rdev->input_id.vendor = VENDOR_ID_FINTEK;
drivers/media/rc/gpio-ir-recv.c +1 -1
index b4f773b..561c27a 100644
@@ -150,7 +150,7 @@ static int gpio_ir_recv_probe(struct platform_device *pdev)
 	}
 
 	rcdev->priv = gpio_dev;
-	rcdev->input_name = GPIO_IR_DEVICE_NAME;
+	rcdev->device_name = GPIO_IR_DEVICE_NAME;
 	rcdev->input_phys = GPIO_IR_DEVICE_NAME "/input0";
 	rcdev->input_id.bustype = BUS_HOST;
 	rcdev->input_id.vendor = 0x0001;
drivers/media/rc/igorplugusb.c +1 -1
index cb6d4f1..5babc63 100644
@@ -194,7 +194,7 @@ static int igorplugusb_probe(struct usb_interface *intf,
 	if (!rc)
 		goto fail;
 
-	rc->input_name = DRIVER_DESC;
+	rc->device_name = DRIVER_DESC;
 	rc->input_phys = ir->phys;
 	usb_to_input_id(udev, &rc->input_id);
 	rc->dev.parent = &intf->dev;
drivers/media/rc/iguanair.c +1 -1
index 8711a7f..4357dd3 100644
@@ -487,7 +487,7 @@ static int iguanair_probe(struct usb_interface *intf,
 
 	usb_make_path(ir->udev, ir->phys, sizeof(ir->phys));
 
-	rc->input_name = ir->name;
+	rc->device_name = ir->name;
 	rc->input_phys = ir->phys;
 	usb_to_input_id(ir->udev, &rc->input_id);
 	rc->dev.parent = &intf->dev;
drivers/media/rc/img-ir/img-ir-hw.c +1 -1
index 8d14396..dd46973 100644
@@ -1083,7 +1083,7 @@ int img_ir_probe_hw(struct img_ir_priv *priv)
 	rdev->priv = priv;
 	rdev->map_name = RC_MAP_EMPTY;
 	rdev->allowed_protocols = img_ir_allowed_protos(priv);
-	rdev->input_name = "IMG Infrared Decoder";
+	rdev->device_name = "IMG Infrared Decoder";
 	rdev->s_filter = img_ir_set_normal_filter;
 	rdev->s_wakeup_filter = img_ir_set_wakeup_filter;
 
drivers/media/rc/img-ir/img-ir-raw.c +1 -1
index 8d2f8e2..7f23a86 100644
@@ -117,7 +117,7 @@ int img_ir_probe_raw(struct img_ir_priv *priv)
 	}
 	rdev->priv = priv;
 	rdev->map_name = RC_MAP_EMPTY;
-	rdev->input_name = "IMG Infrared Decoder Raw";
+	rdev->device_name = "IMG Infrared Decoder Raw";
 
 	/* Register raw decoder */
 	error = rc_register_device(rdev);
drivers/media/rc/imon.c +1 -1
index bd76534..46e1419 100644
@@ -2063,7 +2063,7 @@ static struct rc_dev *imon_init_rdev(struct imon_context *ictx)
 		      sizeof(ictx->phys_rdev));
 	strlcat(ictx->phys_rdev, "/input0", sizeof(ictx->phys_rdev));
 
-	rdev->input_name = ictx->name_rdev;
+	rdev->device_name = ictx->name_rdev;
 	rdev->input_phys = ictx->phys_rdev;
 	usb_to_input_id(ictx->usbdev_intf0, &rdev->input_id);
 	rdev->dev.parent = ictx->dev;
drivers/media/rc/ir-hix5hd2.c +1 -1
index 50951f6..0e639fb 100644
@@ -249,7 +249,7 @@ static int hix5hd2_ir_probe(struct platform_device *pdev)
 	rdev->driver_name = IR_HIX5HD2_NAME;
 	map_name = of_get_property(node, "linux,rc-map-name", NULL);
 	rdev->map_name = map_name ?: RC_MAP_EMPTY;
-	rdev->input_name = IR_HIX5HD2_NAME;
+	rdev->device_name = IR_HIX5HD2_NAME;
 	rdev->input_phys = IR_HIX5HD2_NAME "/input0";
 	rdev->input_id.bustype = BUS_HOST;
 	rdev->input_id.vendor = 0x0001;
drivers/media/rc/ir-spi.c +1 -0
index 7e383b3..29ed063 100644
@@ -155,6 +155,7 @@ static int ir_spi_probe(struct spi_device *spi)
 	idata->rc->tx_ir           = ir_spi_tx;
 	idata->rc->s_tx_carrier    = ir_spi_set_tx_carrier;
 	idata->rc->s_tx_duty_cycle = ir_spi_set_duty_cycle;
+	idata->rc->device_name	   = "IR SPI";
 	idata->rc->driver_name     = IR_SPI_DRIVER_NAME;
 	idata->rc->priv            = idata;
 	idata->spi                 = spi;
drivers/media/rc/ite-cir.c +1 -1
index e9e4bef..c8eea30 100644
@@ -1576,7 +1576,7 @@ static int ite_probe(struct pnp_dev *pdev, const struct pnp_device_id
 		rdev->s_tx_duty_cycle = ite_set_tx_duty_cycle;
 	}
 
-	rdev->input_name = dev_desc->model;
+	rdev->device_name = dev_desc->model;
 	rdev->input_id.bustype = BUS_HOST;
 	rdev->input_id.vendor = PCI_VENDOR_ID_ITE;
 	rdev->input_id.product = 0;
drivers/media/rc/mceusb.c +1 -1
index eb13069..d9c7bbd 100644
@@ -1264,7 +1264,7 @@ static struct rc_dev *mceusb_init_rc_dev(struct mceusb_dev *ir)
 
 	usb_make_path(ir->usbdev, ir->phys, sizeof(ir->phys));
 
-	rc->input_name = ir->name;
+	rc->device_name = ir->name;
 	rc->input_phys = ir->phys;
 	usb_to_input_id(ir->usbdev, &rc->input_id);
 	rc->dev.parent = dev;
drivers/media/rc/meson-ir.c +1 -1
index 65566d5..dfe3da4 100644
@@ -138,7 +138,7 @@ static int meson_ir_probe(struct platform_device *pdev)
 	}
 
 	ir->rc->priv = ir;
-	ir->rc->input_name = DRIVER_NAME;
+	ir->rc->device_name = DRIVER_NAME;
 	ir->rc->input_phys = DRIVER_NAME "/input0";
 	ir->rc->input_id.bustype = BUS_HOST;
 	map_name = of_get_property(node, "linux,rc-map-name", NULL);
drivers/media/rc/mtk-cir.c +1 -1
index f1e164e..a008953 100644
@@ -228,7 +228,7 @@ static int mtk_ir_probe(struct platform_device *pdev)
 	}
 
 	ir->rc->priv = ir;
-	ir->rc->input_name = MTK_IR_DEV;
+	ir->rc->device_name = MTK_IR_DEV;
 	ir->rc->input_phys = MTK_IR_DEV "/input0";
 	ir->rc->input_id.bustype = BUS_HOST;
 	ir->rc->input_id.vendor = 0x0001;
drivers/media/rc/nuvoton-cir.c +1 -1
index ec4b25b..c44f723 100644
@@ -1134,7 +1134,7 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id)
 	rdev->tx_ir = nvt_tx_ir;
 	rdev->s_tx_carrier = nvt_set_tx_carrier;
 	rdev->s_wakeup_filter = nvt_ir_raw_set_wakeup_filter;
-	rdev->input_name = "Nuvoton w836x7hg Infrared Remote Transceiver";
+	rdev->device_name = "Nuvoton w836x7hg Infrared Remote Transceiver";
 	rdev->input_phys = "nuvoton/cir0";
 	rdev->input_id.bustype = BUS_HOST;
 	rdev->input_id.vendor = PCI_VENDOR_ID_WINBOND2;
drivers/media/rc/rc-loopback.c +1 -1
index 62195af..46cc9c2 100644
@@ -219,7 +219,7 @@ static int __init loop_init(void)
 		return -ENOMEM;
 	}
 
-	rc->input_name		= "rc-core loopback device";
+	rc->device_name		= "rc-core loopback device";
 	rc->input_phys		= "rc-core/virtual";
 	rc->input_id.bustype	= BUS_VIRTUAL;
 	rc->input_id.version	= 1;
drivers/media/rc/rc-main.c +4 -4
index a9eba00..a0f852d 100644
@@ -530,7 +530,7 @@ u32 rc_g_keycode_from_table(struct rc_dev *dev, u32 scancode)
 
 	if (keycode != KEY_RESERVED)
 		IR_dprintk(1, "%s: scancode 0x%04x keycode 0x%02x\n",
-			   dev->input_name, scancode, keycode);
+			   dev->device_name, scancode, keycode);
 
 	return keycode;
 }
@@ -663,7 +663,7 @@ static void ir_do_keydown(struct rc_dev *dev, enum rc_type protocol,
 		dev->last_keycode = keycode;
 
 		IR_dprintk(1, "%s: key down event, key 0x%04x, protocol 0x%04x, scancode 0x%08x\n",
-			   dev->input_name, keycode, protocol, scancode);
+			   dev->device_name, keycode, protocol, scancode);
 		input_report_key(dev->input_dev, keycode, 1);
 
 		led_trigger_event(led_feedback, LED_FULL);
@@ -1663,7 +1663,7 @@ static int rc_prepare_rx_device(struct rc_dev *dev)
 	dev->input_dev->dev.parent = &dev->dev;
 	memcpy(&dev->input_dev->id, &dev->input_id, sizeof(dev->input_id));
 	dev->input_dev->phys = dev->input_phys;
-	dev->input_dev->name = dev->input_name;
+	dev->input_dev->name = dev->device_name;
 
 	return 0;
 
@@ -1759,7 +1759,7 @@ int rc_register_device(struct rc_dev *dev)
 
 	path = kobject_get_path(&dev->dev.kobj, GFP_KERNEL);
 	dev_info(&dev->dev, "%s as %s\n",
-		dev->input_name ?: "Unspecified device", path ?: "N/A");
+		 dev->device_name ?: "Unspecified device", path ?: "N/A");
 	kfree(path);
 
 	if (dev->driver_type != RC_DRIVER_IR_RAW_TX) {
drivers/media/rc/redrat3.c +1 -1
index 56d43be..29fa37b 100644
@@ -951,7 +951,7 @@ static struct rc_dev *redrat3_init_rc_dev(struct redrat3_dev *rr3)
 
 	usb_make_path(rr3->udev, rr3->phys, sizeof(rr3->phys));
 
-	rc->input_name = rr3->name;
+	rc->device_name = rr3->name;
 	rc->input_phys = rr3->phys;
 	usb_to_input_id(rr3->udev, &rc->input_id);
 	rc->dev.parent = dev;
drivers/media/rc/serial_ir.c +5 -5
index 77d5d4c..9a5e9fa 100644
@@ -513,19 +513,19 @@ static int serial_ir_probe(struct platform_device *dev)
 
 	switch (type) {
 	case IR_HOMEBREW:
-		rcdev->input_name = "Serial IR type home-brew";
+		rcdev->device_name = "Serial IR type home-brew";
 		break;
 	case IR_IRDEO:
-		rcdev->input_name = "Serial IR type IRdeo";
+		rcdev->device_name = "Serial IR type IRdeo";
 		break;
 	case IR_IRDEO_REMOTE:
-		rcdev->input_name = "Serial IR type IRdeo remote";
+		rcdev->device_name = "Serial IR type IRdeo remote";
 		break;
 	case IR_ANIMAX:
-		rcdev->input_name = "Serial IR type AnimaX";
+		rcdev->device_name = "Serial IR type AnimaX";
 		break;
 	case IR_IGOR:
-		rcdev->input_name = "Serial IR type IgorPlug";
+		rcdev->device_name = "Serial IR type IgorPlug";
 		break;
 	}
 
drivers/media/rc/sir_ir.c +1 -1
index 20234ba..221fce4 100644
@@ -308,7 +308,7 @@ static int sir_ir_probe(struct platform_device *dev)
 	if (!rcdev)
 		return -ENOMEM;
 
-	rcdev->input_name = "SIR IrDA port";
+	rcdev->device_name = "SIR IrDA port";
 	rcdev->input_phys = KBUILD_MODNAME "/input0";
 	rcdev->input_id.bustype = BUS_HOST;
 	rcdev->input_id.vendor = 0x0001;
drivers/media/rc/st_rc.c +1 -1
index a08e1dd..272de9c 100644
@@ -299,7 +299,7 @@ static int st_rc_probe(struct platform_device *pdev)
 	rdev->close = st_rc_close;
 	rdev->driver_name = IR_ST_NAME;
 	rdev->map_name = RC_MAP_EMPTY;
-	rdev->input_name = "ST Remote Control Receiver";
+	rdev->device_name = "ST Remote Control Receiver";
 
 	ret = rc_register_device(rdev);
 	if (ret < 0)
drivers/media/rc/streamzap.c +1 -1
index b09c45a..829f2b3 100644
@@ -299,7 +299,7 @@ static struct rc_dev *streamzap_init_rc_dev(struct streamzap_ir *sz)
 	usb_make_path(sz->usbdev, sz->phys, sizeof(sz->phys));
 	strlcat(sz->phys, "/input0", sizeof(sz->phys));
 
-	rdev->input_name = sz->name;
+	rdev->device_name = sz->name;
 	rdev->input_phys = sz->phys;
 	usb_to_input_id(sz->usbdev, &rdev->input_id);
 	rdev->dev.parent = dev;
drivers/media/rc/sunxi-cir.c +1 -1
index 4b785dd..87933eb 100644
@@ -215,7 +215,7 @@ static int sunxi_ir_probe(struct platform_device *pdev)
 	}
 
 	ir->rc->priv = ir;
-	ir->rc->input_name = SUNXI_IR_DEV;
+	ir->rc->device_name = SUNXI_IR_DEV;
 	ir->rc->input_phys = "sunxi-ir/input0";
 	ir->rc->input_id.bustype = BUS_HOST;
 	ir->rc->input_id.vendor = 0x0001;
drivers/media/rc/ttusbir.c +1 -1
index 23be770..5002a91 100644
@@ -309,7 +309,7 @@ static int ttusbir_probe(struct usb_interface *intf,
 
 	usb_make_path(tt->udev, tt->phys, sizeof(tt->phys));
 
-	rc->input_name = DRIVER_DESC;
+	rc->device_name = DRIVER_DESC;
 	rc->input_phys = tt->phys;
 	usb_to_input_id(tt->udev, &rc->input_id);
 	rc->dev.parent = &intf->dev;
drivers/media/rc/winbond-cir.c +1 -1
index 5a4d4a6..ea7be6d 100644
@@ -1068,7 +1068,7 @@ wbcir_probe(struct pnp_dev *device, const struct pnp_device_id *dev_id)
 	}
 
 	data->dev->driver_name = DRVNAME;
-	data->dev->input_name = WBCIR_NAME;
+	data->dev->device_name = WBCIR_NAME;
 	data->dev->input_phys = "wbcir/cir0";
 	data->dev->input_id.bustype = BUS_HOST;
 	data->dev->input_id.vendor = PCI_VENDOR_ID_WINBOND;
drivers/media/usb/au0828/au0828-input.c +1 -1
index 9d82ec0..9ae42eb 100644
@@ -335,7 +335,7 @@ int au0828_rc_register(struct au0828_dev *dev)
 	usb_make_path(dev->usbdev, ir->phys, sizeof(ir->phys));
 	strlcat(ir->phys, "/input0", sizeof(ir->phys));
 
-	rc->input_name = ir->name;
+	rc->device_name = ir->name;
 	rc->input_phys = ir->phys;
 	rc->input_id.bustype = BUS_USB;
 	rc->input_id.version = 1;
drivers/media/usb/dvb-usb-v2/dvb_usb_core.c +2 -3
index 955fb0d..096bb75 100644
@@ -154,13 +154,12 @@ static int dvb_usbv2_remote_init(struct dvb_usb_device *d)
 	}
 
 	dev->dev.parent = &d->udev->dev;
-	dev->input_name = d->name;
+	dev->device_name = d->name;
 	usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys));
 	strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys));
 	dev->input_phys = d->rc_phys;
 	usb_to_input_id(d->udev, &dev->input_id);
-	/* TODO: likely RC-core should took const char * */
-	dev->driver_name = (char *) d->props->driver_name;
+	dev->driver_name = d->props->driver_name;
 	dev->map_name = d->rc.map_name;
 	dev->allowed_protocols = d->rc.allowed_protos;
 	dev->change_protocol = d->rc.change_protocol;
drivers/media/usb/dvb-usb/dvb-usb-remote.c +1 -1
index f05f1fc..0b03f9b 100644
@@ -279,7 +279,7 @@ static int rc_core_dvb_usb_remote_init(struct dvb_usb_device *d)
 	dev->change_protocol = d->props.rc.core.change_protocol;
 	dev->allowed_protocols = d->props.rc.core.allowed_protos;
 	usb_to_input_id(d->udev, &dev->input_id);
-	dev->input_name = "IR-receiver inside an USB DVB receiver";
+	dev->device_name = "IR-receiver inside an USB DVB receiver";
 	dev->input_phys = d->rc_phys;
 	dev->dev.parent = &d->udev->dev;
 	dev->priv = d;
drivers/media/usb/em28xx/em28xx-input.c +1 -1
index ca96739..d8746b9 100644
@@ -807,7 +807,7 @@ static int em28xx_ir_init(struct em28xx *dev)
 	usb_make_path(udev, ir->phys, sizeof(ir->phys));
 	strlcat(ir->phys, "/input0", sizeof(ir->phys));
 
-	rc->input_name = ir->name;
+	rc->device_name = ir->name;
 	rc->input_phys = ir->phys;
 	rc->input_id.bustype = BUS_USB;
 	rc->input_id.version = 1;
drivers/media/usb/tm6000/tm6000-input.c +1 -1
index 1a033f5..83e33ae 100644
@@ -458,7 +458,7 @@ int tm6000_ir_init(struct tm6000_core *dev)
 	rc_type = RC_BIT_UNKNOWN;
 	tm6000_ir_change_protocol(rc, &rc_type);
 
-	rc->input_name = ir->name;
+	rc->device_name = ir->name;
 	rc->input_phys = ir->phys;
 	rc->input_id.bustype = BUS_USB;
 	rc->input_id.version = 1;
include/media/cec.h +1 -1
index 56643b2..3aecd0d 100644
@@ -184,7 +184,7 @@ struct cec_adapter {
 	u16 phys_addrs[15];
 	u32 sequence;
 
-	char input_name[32];
+	char device_name[32];
 	char input_phys[32];
 	char input_drv[32];
 };
include/media/rc-core.h +3 -3
index 78dea39..16bd89c 100644
@@ -72,7 +72,7 @@ enum rc_filter_type {
  * @dev: driver model's view of this device
  * @managed_alloc: devm_rc_allocate_device was used to create rc_dev
  * @sysfs_groups: sysfs attribute groups
- * @input_name: name of the input child device
+ * @device_name: name of the rc child device
  * @input_phys: physical path to the input child device
  * @input_id: id of the input child device (struct input_id)
  * @driver_name: name of the hardware driver which registered this device
@@ -138,10 +138,10 @@ struct rc_dev {
 	struct device			dev;
 	bool				managed_alloc;
 	const struct attribute_group	*sysfs_groups[5];
-	const char			*input_name;
+	const char			*device_name;
 	const char			*input_phys;
 	struct input_id			input_id;
-	char				*driver_name;
+	const char			*driver_name;
 	const char			*map_name;
 	struct rc_map			rc_map;
 	struct mutex			lock;
Signature · 1 line
2.9.4
author maintainer
#2 lore raw

PATCH v2 2/6 rc: mce kbd decoder not needed for IR TX drivers

In reply to Sean Young

Without this patch, an input device is created which is not necessary.

---
 drivers/media/rc/ir-mce_kbd-decoder.c | 3 +++
 1 file changed, 3 insertions(+)
drivers/media/rc/ir-mce_kbd-decoder.c +3 -0
index 6a4d58b..0e07442 100644
@@ -358,6 +358,9 @@ static int ir_mce_kbd_register(struct rc_dev *dev)
 	struct input_dev *idev;
 	int i, ret;
 
+	if (dev->driver_type == RC_DRIVER_IR_RAW_TX)
+		return 0;
+
 	idev = input_allocate_device();
 	if (!idev)
 		return -ENOMEM;
Signature · 1 line
2.9.4
author maintainer
#3 lore raw

PATCH v2 3/6 rc: gpio-ir-tx: add new driver

In reply to Sean Young

This is a simple bit-banging GPIO IR TX driver.

---
 MAINTAINERS                   |   6 ++
 drivers/media/rc/Kconfig      |  11 +++
 drivers/media/rc/Makefile     |   1 +
 drivers/media/rc/gpio-ir-tx.c | 189 ++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 207 insertions(+)
 create mode 100644 drivers/media/rc/gpio-ir-tx.c
MAINTAINERS +6 -0
index c4be6d4..8a37c2f 100644
@@ -5667,6 +5667,12 @@ L:	[email protected]
 S:	Maintained
 F:	drivers/input/touchscreen/goodix.c
 
+GPIO IR Transmitter
+M:	Sean Young <[email protected]>
+L:	[email protected]
+S:	Maintained
+F:	drivers/media/rc/gpio-ir-tx.c
+
 GPIO MOCKUP DRIVER
 M:	Bamvor Jian Zhang <[email protected]>
 L:	[email protected]
drivers/media/rc/Kconfig +11 -0
index b6433c5..ef767d1 100644
@@ -399,6 +399,17 @@ config IR_GPIO_CIR
 	   To compile this driver as a module, choose M here: the module will
 	   be called gpio-ir-recv.
 
+config IR_GPIO_TX
+	tristate "GPIO IR bit banging transmitter"
+	depends on RC_CORE
+	depends on LIRC
+	---help---
+	   Say Y if you want to a GPIO based IR transmitter. This is a
+	   bit banging driver.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called gpio-ir-tx.
+
 config RC_ST
 	tristate "ST remote control receiver"
 	depends on RC_CORE
drivers/media/rc/Makefile +1 -0
index 245e2c2..3e64a4e 100644
@@ -32,6 +32,7 @@ obj-$(CONFIG_IR_STREAMZAP) += streamzap.o
 obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o
 obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o
 obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o
+obj-$(CONFIG_IR_GPIO_TX) += gpio-ir-tx.o
 obj-$(CONFIG_IR_IGORPLUGUSB) += igorplugusb.o
 obj-$(CONFIG_IR_IGUANA) += iguanair.o
 obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o
drivers/media/rc/gpio-ir-tx.c new file +189 -0

189 lines not on this page · raw message

Signature · 1 line
2.9.4
#4 lore raw

Re: [PATCH v2 3/6] [media] rc: gpio-ir-tx: add new driver

Hi Sean,

Quoted 1 line · Sean Young

This is a simple bit-banging GPIO IR TX driver.

thanks a lot for the driver, this is highly appreciated!

I tested the patch series on a RPi2, against RPi downstream kernel 4.13-rc1, and noticed an issue: the polarity of the gpio seems to be reversed.

Other than the polarity issue the driver seems to work fine - at least on the scope screen. Didn't have an IR transmitter to do some real tests yet.

I've configured the gpio as active high:

gpio_ir_tx: gpio-ir-transmitter {
	compatible = "gpio-ir-tx";
	gpios = <&gpio 18 0>;
};

However, when loading the gpio-ir-tx driver the gpio pin changed to 3.3V. I did some tests with ir-ctl -S, idle state of the signal was 3.3V, active state 0V.

I think it's better to use the descriptor based gpio functions instead of the legacy number based ones. That'll simplify the driver and it can delegate polarity handling to gpiod.

Proposed changes and comments are inline. I've also included the patch against your patch that I've been testing with at the end of the message.

Quoted 12 lines
drivers/media/rc/gpio-ir-tx.c new file +6 -0
new file mode 100644
index 0000000..7a5371d
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2017 Sean Young <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, or
+ * (at your option) any later version.

drivers/media/rc/gpio-ir-tx.c line 16

Quoted 10 lines
drivers/media/rc/gpio-ir-tx.c new file +10 -0
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio.h>

use linux/gpio/consumer.h instead of linux/gpio.h

drivers/media/rc/gpio-ir-tx.c line 20

Quoted 4 lines
drivers/media/rc/gpio-ir-tx.c new file +4 -0
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>

of_gpio.h can be dropped

drivers/media/rc/gpio-ir-tx.c line 29

Quoted 9 lines
drivers/media/rc/gpio-ir-tx.c new file +9 -0
+#include <linux/platform_device.h>
+#include <media/rc-core.h>
+
+#define DRIVER_NAME	"gpio-ir-tx"
+#define DEVICE_NAME	"GPIO Bit Banging IR Transmitter"
+
+struct gpio_ir {
+	int gpio_nr;
+	bool active_low;

Replace these 2 fields with

	struct gpio_desc *gpio;
Quoted 66 lines
drivers/media/rc/gpio-ir-tx.c new file +66 -0
+	unsigned int carrier;
+	unsigned int duty_cycle;
+	/* we need a spinlock to hold the cpu while transmitting */
+	spinlock_t lock;
+};
+
+static const struct of_device_id gpio_ir_tx_of_match[] = {
+	{ .compatible = "gpio-ir-tx", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, gpio_ir_tx_of_match);
+
+static int gpio_ir_tx_set_duty_cycle(struct rc_dev *dev, u32 duty_cycle)
+{
+	struct gpio_ir *gpio_ir = dev->priv;
+
+	gpio_ir->duty_cycle = duty_cycle;
+
+	return 0;
+}
+
+static int gpio_ir_tx_set_carrier(struct rc_dev *dev, u32 carrier)
+{
+	struct gpio_ir *gpio_ir = dev->priv;
+
+	if (!carrier)
+		return -EINVAL;
+
+	gpio_ir->carrier = carrier;
+
+	return 0;
+}
+
+static int gpio_ir_tx(struct rc_dev *dev, unsigned int *txbuf,
+		      unsigned int count)
+{
+	struct gpio_ir *gpio_ir = dev->priv;
+	unsigned long flags;
+	ktime_t edge;
+	/*
+	 * delta should never exceed 0.5 seconds (IR_MAX_DURATION) and on
+	 * m68k ndelay(s64) does not compile; so use s32 rather than s64.
+	 */
+	s32 delta;
+	int i;
+	unsigned int pulse, space;
+
+	/* Ensure the dividend fits into 32 bit */
+	pulse = DIV_ROUND_CLOSEST(gpio_ir->duty_cycle * (NSEC_PER_SEC / 100),
+				  gpio_ir->carrier);
+	space = DIV_ROUND_CLOSEST((100 - gpio_ir->duty_cycle) *
+				  (NSEC_PER_SEC / 100), gpio_ir->carrier);
+
+	spin_lock_irqsave(&gpio_ir->lock, flags);
+
+	edge = ktime_get();
+
+	for (i = 0; i < count; i++) {
+		if (i % 2) {
+			// space
+			edge = ktime_add_us(edge, txbuf[i]);
+			delta = ktime_us_delta(edge, ktime_get());
+			if (delta > 10) {
+				spin_unlock_irqrestore(&gpio_ir->lock, flags);
+				usleep_range(delta, delta + 10);
+				spin_lock_irqsave(&gpio_ir->lock, flags);

drivers/media/rc/gpio-ir-tx.c line 105

Quoted 10 lines
drivers/media/rc/gpio-ir-tx.c new file +10 -0
+			} else if (delta > 0) {
+				udelay(delta);
+			}
+		} else {
+			// pulse
+			ktime_t last = ktime_add_us(edge, txbuf[i]);
+
+			while (ktime_get() < last) {
+				gpio_set_value(gpio_ir->gpio_nr,
+					       gpio_ir->active_low);
				gpiod_set_value(gpio_ir->gpio, 1);

drivers/media/rc/gpio-ir-tx.c line 111

Quoted 6 lines
drivers/media/rc/gpio-ir-tx.c new file +6 -0
+				edge += pulse;
+				delta = edge - ktime_get();
+				if (delta > 0)
+					ndelay(delta);
+				gpio_set_value(gpio_ir->gpio_nr,
+					       !gpio_ir->active_low);
				gpiod_set_value(gpio_ir->gpio, 0);
Quoted 11 lines
drivers/media/rc/gpio-ir-tx.c new file +11 -0
+				edge += space;
+				delta = edge - ktime_get();
+				if (delta > 0)
+					ndelay(delta);
+			}
+
+			edge = last;
+		}
+	}
+
+	spin_unlock_irqrestore(&gpio_ir->lock, flags);

drivers/media/rc/gpio-ir-tx.c line 132

Quoted 10 lines
drivers/media/rc/gpio-ir-tx.c new file +10 -0
+
+	return count;
+}
+
+static int gpio_ir_tx_probe(struct platform_device *pdev)
+{
+	struct gpio_ir *gpio_ir;
+	struct rc_dev *rcdev;
+	enum of_gpio_flags flags;
+	int rc, gpio;

Flags can be dropped, gpio as well.

drivers/media/rc/gpio-ir-tx.c line 140

Quoted 8 lines
drivers/media/rc/gpio-ir-tx.c new file +8 -0
+
+	gpio = of_get_gpio_flags(pdev->dev.of_node, 0, &flags);
+	if (gpio < 0) {
+		if (gpio != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "Failed to get gpio flags (%d)\n",
+				gpio);
+		return -EINVAL;
+	}

Drop this and move getting the gpio a bit down so we don't need a temp variable.

drivers/media/rc/gpio-ir-tx.c line 148

Quoted 8 lines
drivers/media/rc/gpio-ir-tx.c new file +8 -0
+
+	gpio_ir = devm_kmalloc(&pdev->dev, sizeof(*gpio_ir), GFP_KERNEL);
+	if (!gpio_ir)
+		return -ENOMEM;
+
+	rcdev = devm_rc_allocate_device(&pdev->dev, RC_DRIVER_IR_RAW_TX);
+	if (!rcdev)
+		return -ENOMEM;

get the gpio here, configure it to output with logical low (idle) level and store it in gpio_ir:

	gpio_ir->gpio = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW);
	if (IS_ERR(gpio_ir->gpio)) {
		if (PTR_ERR(gpio_ir->gpio) != -EPROBE_DEFER)
			dev_err(&pdev->dev, "Failed to get gpio (%ld)\n",
				PTR_ERR(gpio_ir->gpio));
		return PTR_ERR(gpio_ir->gpio);
	}

drivers/media/rc/gpio-ir-tx.c line 158

Quoted 10 lines
drivers/media/rc/gpio-ir-tx.c new file +10 -0
+
+	rcdev->priv = gpio_ir;
+	rcdev->driver_name = DRIVER_NAME;
+	rcdev->device_name = DEVICE_NAME;
+	rcdev->tx_ir = gpio_ir_tx;
+	rcdev->s_tx_duty_cycle = gpio_ir_tx_set_duty_cycle;
+	rcdev->s_tx_carrier = gpio_ir_tx_set_carrier;
+
+	gpio_ir->gpio_nr = gpio;
+	gpio_ir->active_low = (flags & OF_GPIO_ACTIVE_LOW) != 0;

drop gpio_nr and active_low

drivers/media/rc/gpio-ir-tx.c line 169

Quoted 11 lines
drivers/media/rc/gpio-ir-tx.c new file +11 -0
+	gpio_ir->carrier = 38000;
+	gpio_ir->duty_cycle = 50;
+	spin_lock_init(&gpio_ir->lock);
+
+	rc = devm_gpio_request(&pdev->dev, gpio, "gpio-ir-tx");
+	if (rc < 0)
+		return rc;
+
+	rc = gpio_direction_output(gpio, !gpio_ir->active_low);
+	if (rc < 0)
+		return rc;

drop devm_gpio_request and gpio_direction_output, that is already handled by devm_gpiod_get.

Quoted 22 lines
drivers/media/rc/gpio-ir-tx.c new file +20 -0
+
+	rc = devm_rc_register_device(&pdev->dev, rcdev);
+	if (rc < 0)
+		dev_err(&pdev->dev, "failed to register rc device\n");
+
+	return rc;
+}
+
+static struct platform_driver gpio_ir_tx_driver = {
+	.probe	= gpio_ir_tx_probe,
+	.driver = {
+		.name	= DRIVER_NAME,
+		.of_match_table = of_match_ptr(gpio_ir_tx_of_match),
+	},
+};
+module_platform_driver(gpio_ir_tx_driver);
+
+MODULE_DESCRIPTION("GPIO Bit Banging IR Transmitter");
+MODULE_AUTHOR("Sean Young <[email protected]>");
+MODULE_LICENSE("GPL");
Signature · 1 line
2.9.4

so long,

Hias
---

drivers/media/rc/gpio-ir-tx.c +13 -28
index 7a5371dbb360..ca6834d09467 100644
@@ -13,11 +13,10 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/of.h>
-#include <linux/of_gpio.h>
 #include <linux/platform_device.h>
 #include <media/rc-core.h>
 
@@ -25,8 +24,7 @@
 #define DEVICE_NAME	"GPIO Bit Banging IR Transmitter"
 
 struct gpio_ir {
-	int gpio_nr;
-	bool active_low;
+	struct gpio_desc *gpio;
 	unsigned int carrier;
 	unsigned int duty_cycle;
 	/* we need a spinlock to hold the cpu while transmitting */
@@ -101,14 +99,12 @@ static int gpio_ir_tx(struct rc_dev *dev, unsigned int *txbuf,
 			ktime_t last = ktime_add_us(edge, txbuf[i]);
 
 			while (ktime_get() < last) {
-				gpio_set_value(gpio_ir->gpio_nr,
-					       gpio_ir->active_low);
+				gpiod_set_value(gpio_ir->gpio, 1);
 				edge += pulse;
 				delta = edge - ktime_get();
 				if (delta > 0)
 					ndelay(delta);
-				gpio_set_value(gpio_ir->gpio_nr,
-					       !gpio_ir->active_low);
+				gpiod_set_value(gpio_ir->gpio, 0);
 				edge += space;
 				delta = edge - ktime_get();
 				if (delta > 0)
@@ -128,16 +124,7 @@ static int gpio_ir_tx_probe(struct platform_device *pdev)
 {
 	struct gpio_ir *gpio_ir;
 	struct rc_dev *rcdev;
-	enum of_gpio_flags flags;
-	int rc, gpio;
-
-	gpio = of_get_gpio_flags(pdev->dev.of_node, 0, &flags);
-	if (gpio < 0) {
-		if (gpio != -EPROBE_DEFER)
-			dev_err(&pdev->dev, "Failed to get gpio flags (%d)\n",
-				gpio);
-		return -EINVAL;
-	}
+	int rc;
 
 	gpio_ir = devm_kmalloc(&pdev->dev, sizeof(*gpio_ir), GFP_KERNEL);
 	if (!gpio_ir)
@@ -147,6 +134,14 @@ static int gpio_ir_tx_probe(struct platform_device *pdev)
 	if (!rcdev)
 		return -ENOMEM;
 
+	gpio_ir->gpio = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW);
+	if (IS_ERR(gpio_ir->gpio)) {
+		if (PTR_ERR(gpio_ir->gpio) != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "Failed to get gpio (%ld)\n",
+				PTR_ERR(gpio_ir->gpio));
+		return PTR_ERR(gpio_ir->gpio);
+	}
+
 	rcdev->priv = gpio_ir;
 	rcdev->driver_name = DRIVER_NAME;
 	rcdev->device_name = DEVICE_NAME;
@@ -154,20 +149,10 @@ static int gpio_ir_tx_probe(struct platform_device *pdev)
 	rcdev->s_tx_duty_cycle = gpio_ir_tx_set_duty_cycle;
 	rcdev->s_tx_carrier = gpio_ir_tx_set_carrier;
 
-	gpio_ir->gpio_nr = gpio;
-	gpio_ir->active_low = (flags & OF_GPIO_ACTIVE_LOW) != 0;
 	gpio_ir->carrier = 38000;
 	gpio_ir->duty_cycle = 50;
 	spin_lock_init(&gpio_ir->lock);
 
-	rc = devm_gpio_request(&pdev->dev, gpio, "gpio-ir-tx");
-	if (rc < 0)
-		return rc;
-
-	rc = gpio_direction_output(gpio, !gpio_ir->active_low);
-	if (rc < 0)
-		return rc;
-
 	rc = devm_rc_register_device(&pdev->dev, rcdev);
 	if (rc < 0)
 		dev_err(&pdev->dev, "failed to register rc device\n");
#5 lore raw

Re: [PATCH v2 3/6] [media] rc: gpio-ir-tx: add new driver

Hi Sean,

Quoted 7 lines · Matthias Reichl

Hi Sean,

Quoted 1 line · Sean Young

This is a simple bit-banging GPIO IR TX driver.

thanks a lot for the driver, this is highly appreciated!

Quoted 7 lines

I tested the patch series on a RPi2, against RPi downstream kernel 4.13-rc1, and noticed an issue: the polarity of the gpio seems to be reversed.

Other than the polarity issue the driver seems to work fine - at least on the scope screen. Didn't have an IR transmitter to do some real tests yet.

I've now run a functional test and it worked fine.

I tested with your patch and my proposed changes, using an active-high IR transmitter and ir-ctl -S rc5:... to send rc-5 codes and another RPi with gpio-ir-recv to receive/decode them.

so long & thanks a lot,

Hias

Quoted 362 lines

I've configured the gpio as active high:

gpio_ir_tx: gpio-ir-transmitter {
	compatible = "gpio-ir-tx";
	gpios = <&gpio 18 0>;
};

However, when loading the gpio-ir-tx driver the gpio pin changed to 3.3V. I did some tests with ir-ctl -S, idle state of the signal was 3.3V, active state 0V.

I think it's better to use the descriptor based gpio functions instead of the legacy number based ones. That'll simplify the driver and it can delegate polarity handling to gpiod.

Proposed changes and comments are inline. I've also included the patch against your patch that I've been testing with at the end of the message.

Quoted 12 lines
drivers/media/rc/gpio-ir-tx.c new file +6 -0
new file mode 100644
index 0000000..7a5371d
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2017 Sean Young <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, or
+ * (at your option) any later version.

drivers/media/rc/gpio-ir-tx.c line 16

Quoted 10 lines
drivers/media/rc/gpio-ir-tx.c new file +10 -0
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio.h>

use linux/gpio/consumer.h instead of linux/gpio.h

drivers/media/rc/gpio-ir-tx.c line 20

Quoted 4 lines
drivers/media/rc/gpio-ir-tx.c new file +4 -0
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>

of_gpio.h can be dropped

drivers/media/rc/gpio-ir-tx.c line 29

Quoted 9 lines
drivers/media/rc/gpio-ir-tx.c new file +9 -0
+#include <linux/platform_device.h>
+#include <media/rc-core.h>
+
+#define DRIVER_NAME	"gpio-ir-tx"
+#define DEVICE_NAME	"GPIO Bit Banging IR Transmitter"
+
+struct gpio_ir {
+	int gpio_nr;
+	bool active_low;

Replace these 2 fields with

	struct gpio_desc *gpio;
Quoted 66 lines
drivers/media/rc/gpio-ir-tx.c new file +66 -0
+	unsigned int carrier;
+	unsigned int duty_cycle;
+	/* we need a spinlock to hold the cpu while transmitting */
+	spinlock_t lock;
+};
+
+static const struct of_device_id gpio_ir_tx_of_match[] = {
+	{ .compatible = "gpio-ir-tx", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, gpio_ir_tx_of_match);
+
+static int gpio_ir_tx_set_duty_cycle(struct rc_dev *dev, u32 duty_cycle)
+{
+	struct gpio_ir *gpio_ir = dev->priv;
+
+	gpio_ir->duty_cycle = duty_cycle;
+
+	return 0;
+}
+
+static int gpio_ir_tx_set_carrier(struct rc_dev *dev, u32 carrier)
+{
+	struct gpio_ir *gpio_ir = dev->priv;
+
+	if (!carrier)
+		return -EINVAL;
+
+	gpio_ir->carrier = carrier;
+
+	return 0;
+}
+
+static int gpio_ir_tx(struct rc_dev *dev, unsigned int *txbuf,
+		      unsigned int count)
+{
+	struct gpio_ir *gpio_ir = dev->priv;
+	unsigned long flags;
+	ktime_t edge;
+	/*
+	 * delta should never exceed 0.5 seconds (IR_MAX_DURATION) and on
+	 * m68k ndelay(s64) does not compile; so use s32 rather than s64.
+	 */
+	s32 delta;
+	int i;
+	unsigned int pulse, space;
+
+	/* Ensure the dividend fits into 32 bit */
+	pulse = DIV_ROUND_CLOSEST(gpio_ir->duty_cycle * (NSEC_PER_SEC / 100),
+				  gpio_ir->carrier);
+	space = DIV_ROUND_CLOSEST((100 - gpio_ir->duty_cycle) *
+				  (NSEC_PER_SEC / 100), gpio_ir->carrier);
+
+	spin_lock_irqsave(&gpio_ir->lock, flags);
+
+	edge = ktime_get();
+
+	for (i = 0; i < count; i++) {
+		if (i % 2) {
+			// space
+			edge = ktime_add_us(edge, txbuf[i]);
+			delta = ktime_us_delta(edge, ktime_get());
+			if (delta > 10) {
+				spin_unlock_irqrestore(&gpio_ir->lock, flags);
+				usleep_range(delta, delta + 10);
+				spin_lock_irqsave(&gpio_ir->lock, flags);

drivers/media/rc/gpio-ir-tx.c line 105

Quoted 10 lines
drivers/media/rc/gpio-ir-tx.c new file +10 -0
+			} else if (delta > 0) {
+				udelay(delta);
+			}
+		} else {
+			// pulse
+			ktime_t last = ktime_add_us(edge, txbuf[i]);
+
+			while (ktime_get() < last) {
+				gpio_set_value(gpio_ir->gpio_nr,
+					       gpio_ir->active_low);
				gpiod_set_value(gpio_ir->gpio, 1);

drivers/media/rc/gpio-ir-tx.c line 111

Quoted 6 lines
drivers/media/rc/gpio-ir-tx.c new file +6 -0
+				edge += pulse;
+				delta = edge - ktime_get();
+				if (delta > 0)
+					ndelay(delta);
+				gpio_set_value(gpio_ir->gpio_nr,
+					       !gpio_ir->active_low);
				gpiod_set_value(gpio_ir->gpio, 0);
Quoted 11 lines
drivers/media/rc/gpio-ir-tx.c new file +11 -0
+				edge += space;
+				delta = edge - ktime_get();
+				if (delta > 0)
+					ndelay(delta);
+			}
+
+			edge = last;
+		}
+	}
+
+	spin_unlock_irqrestore(&gpio_ir->lock, flags);

drivers/media/rc/gpio-ir-tx.c line 132

Quoted 10 lines
drivers/media/rc/gpio-ir-tx.c new file +10 -0
+
+	return count;
+}
+
+static int gpio_ir_tx_probe(struct platform_device *pdev)
+{
+	struct gpio_ir *gpio_ir;
+	struct rc_dev *rcdev;
+	enum of_gpio_flags flags;
+	int rc, gpio;

Flags can be dropped, gpio as well.

drivers/media/rc/gpio-ir-tx.c line 140

Quoted 8 lines
drivers/media/rc/gpio-ir-tx.c new file +8 -0
+
+	gpio = of_get_gpio_flags(pdev->dev.of_node, 0, &flags);
+	if (gpio < 0) {
+		if (gpio != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "Failed to get gpio flags (%d)\n",
+				gpio);
+		return -EINVAL;
+	}

Drop this and move getting the gpio a bit down so we don't need a temp variable.

drivers/media/rc/gpio-ir-tx.c line 148

Quoted 8 lines
drivers/media/rc/gpio-ir-tx.c new file +8 -0
+
+	gpio_ir = devm_kmalloc(&pdev->dev, sizeof(*gpio_ir), GFP_KERNEL);
+	if (!gpio_ir)
+		return -ENOMEM;
+
+	rcdev = devm_rc_allocate_device(&pdev->dev, RC_DRIVER_IR_RAW_TX);
+	if (!rcdev)
+		return -ENOMEM;

get the gpio here, configure it to output with logical low (idle) level and store it in gpio_ir:

	gpio_ir->gpio = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW);
	if (IS_ERR(gpio_ir->gpio)) {
		if (PTR_ERR(gpio_ir->gpio) != -EPROBE_DEFER)
			dev_err(&pdev->dev, "Failed to get gpio (%ld)\n",
				PTR_ERR(gpio_ir->gpio));
		return PTR_ERR(gpio_ir->gpio);
	}

drivers/media/rc/gpio-ir-tx.c line 158

Quoted 10 lines
drivers/media/rc/gpio-ir-tx.c new file +10 -0
+
+	rcdev->priv = gpio_ir;
+	rcdev->driver_name = DRIVER_NAME;
+	rcdev->device_name = DEVICE_NAME;
+	rcdev->tx_ir = gpio_ir_tx;
+	rcdev->s_tx_duty_cycle = gpio_ir_tx_set_duty_cycle;
+	rcdev->s_tx_carrier = gpio_ir_tx_set_carrier;
+
+	gpio_ir->gpio_nr = gpio;
+	gpio_ir->active_low = (flags & OF_GPIO_ACTIVE_LOW) != 0;

drop gpio_nr and active_low

drivers/media/rc/gpio-ir-tx.c line 169

Quoted 11 lines
drivers/media/rc/gpio-ir-tx.c new file +11 -0
+	gpio_ir->carrier = 38000;
+	gpio_ir->duty_cycle = 50;
+	spin_lock_init(&gpio_ir->lock);
+
+	rc = devm_gpio_request(&pdev->dev, gpio, "gpio-ir-tx");
+	if (rc < 0)
+		return rc;
+
+	rc = gpio_direction_output(gpio, !gpio_ir->active_low);
+	if (rc < 0)
+		return rc;

drop devm_gpio_request and gpio_direction_output, that is already handled by devm_gpiod_get.

Quoted 22 lines
drivers/media/rc/gpio-ir-tx.c new file +20 -0
+
+	rc = devm_rc_register_device(&pdev->dev, rcdev);
+	if (rc < 0)
+		dev_err(&pdev->dev, "failed to register rc device\n");
+
+	return rc;
+}
+
+static struct platform_driver gpio_ir_tx_driver = {
+	.probe	= gpio_ir_tx_probe,
+	.driver = {
+		.name	= DRIVER_NAME,
+		.of_match_table = of_match_ptr(gpio_ir_tx_of_match),
+	},
+};
+module_platform_driver(gpio_ir_tx_driver);
+
+MODULE_DESCRIPTION("GPIO Bit Banging IR Transmitter");
+MODULE_AUTHOR("Sean Young <[email protected]>");
+MODULE_LICENSE("GPL");
Signature · 1 line
2.9.4

so long,

Hias
---

drivers/media/rc/gpio-ir-tx.c +13 -28
index 7a5371dbb360..ca6834d09467 100644
@@ -13,11 +13,10 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/of.h>
-#include <linux/of_gpio.h>
 #include <linux/platform_device.h>
 #include <media/rc-core.h>
 
@@ -25,8 +24,7 @@
 #define DEVICE_NAME	"GPIO Bit Banging IR Transmitter"
 
 struct gpio_ir {
-	int gpio_nr;
-	bool active_low;
+	struct gpio_desc *gpio;
 	unsigned int carrier;
 	unsigned int duty_cycle;
 	/* we need a spinlock to hold the cpu while transmitting */
@@ -101,14 +99,12 @@ static int gpio_ir_tx(struct rc_dev *dev, unsigned int *txbuf,
 			ktime_t last = ktime_add_us(edge, txbuf[i]);
 
 			while (ktime_get() < last) {
-				gpio_set_value(gpio_ir->gpio_nr,
-					       gpio_ir->active_low);
+				gpiod_set_value(gpio_ir->gpio, 1);
 				edge += pulse;
 				delta = edge - ktime_get();
 				if (delta > 0)
 					ndelay(delta);
-				gpio_set_value(gpio_ir->gpio_nr,
-					       !gpio_ir->active_low);
+				gpiod_set_value(gpio_ir->gpio, 0);
 				edge += space;
 				delta = edge - ktime_get();
 				if (delta > 0)
@@ -128,16 +124,7 @@ static int gpio_ir_tx_probe(struct platform_device *pdev)
 {
 	struct gpio_ir *gpio_ir;
 	struct rc_dev *rcdev;
-	enum of_gpio_flags flags;
-	int rc, gpio;
-
-	gpio = of_get_gpio_flags(pdev->dev.of_node, 0, &flags);
-	if (gpio < 0) {
-		if (gpio != -EPROBE_DEFER)
-			dev_err(&pdev->dev, "Failed to get gpio flags (%d)\n",
-				gpio);
-		return -EINVAL;
-	}
+	int rc;
 
 	gpio_ir = devm_kmalloc(&pdev->dev, sizeof(*gpio_ir), GFP_KERNEL);
 	if (!gpio_ir)
@@ -147,6 +134,14 @@ static int gpio_ir_tx_probe(struct platform_device *pdev)
 	if (!rcdev)
 		return -ENOMEM;
 
+	gpio_ir->gpio = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW);
+	if (IS_ERR(gpio_ir->gpio)) {
+		if (PTR_ERR(gpio_ir->gpio) != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "Failed to get gpio (%ld)\n",
+				PTR_ERR(gpio_ir->gpio));
+		return PTR_ERR(gpio_ir->gpio);
+	}
+
 	rcdev->priv = gpio_ir;
 	rcdev->driver_name = DRIVER_NAME;
 	rcdev->device_name = DEVICE_NAME;
@@ -154,20 +149,10 @@ static int gpio_ir_tx_probe(struct platform_device *pdev)
 	rcdev->s_tx_duty_cycle = gpio_ir_tx_set_duty_cycle;
 	rcdev->s_tx_carrier = gpio_ir_tx_set_carrier;
 
-	gpio_ir->gpio_nr = gpio;
-	gpio_ir->active_low = (flags & OF_GPIO_ACTIVE_LOW) != 0;
 	gpio_ir->carrier = 38000;
 	gpio_ir->duty_cycle = 50;
 	spin_lock_init(&gpio_ir->lock);
 
-	rc = devm_gpio_request(&pdev->dev, gpio, "gpio-ir-tx");
-	if (rc < 0)
-		return rc;
-
-	rc = gpio_direction_output(gpio, !gpio_ir->active_low);
-	if (rc < 0)
-		return rc;
-
 	rc = devm_rc_register_device(&pdev->dev, rcdev);
 	if (rc < 0)
 		dev_err(&pdev->dev, "failed to register rc device\n");
author maintainer
#6 lore raw

Re: [PATCH v2 3/6] [media] rc: gpio-ir-tx: add new driver

In reply to Matthias Reichl

Hi Matthias,

Quoted 22 lines · Matthias Reichl

Hi Sean,

Quoted 1 line · Sean Young

This is a simple bit-banging GPIO IR TX driver.

thanks a lot for the driver, this is highly appreciated!

I tested the patch series on a RPi2, against RPi downstream kernel 4.13-rc1, and noticed an issue: the polarity of the gpio seems to be reversed.

Other than the polarity issue the driver seems to work fine - at least on the scope screen. Didn't have an IR transmitter to do some real tests yet.

I've configured the gpio as active high:

gpio_ir_tx: gpio-ir-transmitter {
	compatible = "gpio-ir-tx";
	gpios = <&gpio 18 0>;
};
Quoted 7 lines

However, when loading the gpio-ir-tx driver the gpio pin changed to 3.3V. I did some tests with ir-ctl -S, idle state of the signal was 3.3V, active state 0V.

I think it's better to use the descriptor based gpio functions instead of the legacy number based ones. That'll simplify the driver and it can delegate polarity handling to gpiod.

You're absolutely right, this is much nicer and makes the driver shorter.

Quoted 3 lines

Proposed changes and comments are inline. I've also included the patch against your patch that I've been testing with at the end of the message.

I agree with all your comments. However, we need a Signed-off-by:
line to use your patch, thank you.

Regards,
Sean

Quoted 232 lines
Quoted 13 lines
drivers/media/rc/gpio-ir-tx.c new file +6 -0
new file mode 100644
index 0000000..7a5371d
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2017 Sean Young <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, or
+ * (at your option) any later version.

drivers/media/rc/gpio-ir-tx.c line 16

Quoted 10 lines
drivers/media/rc/gpio-ir-tx.c new file +10 -0
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/gpio.h>

use linux/gpio/consumer.h instead of linux/gpio.h

drivers/media/rc/gpio-ir-tx.c line 20

Quoted 4 lines
drivers/media/rc/gpio-ir-tx.c new file +4 -0
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>

of_gpio.h can be dropped

drivers/media/rc/gpio-ir-tx.c line 29

Quoted 9 lines
drivers/media/rc/gpio-ir-tx.c new file +9 -0
+#include <linux/platform_device.h>
+#include <media/rc-core.h>
+
+#define DRIVER_NAME	"gpio-ir-tx"
+#define DEVICE_NAME	"GPIO Bit Banging IR Transmitter"
+
+struct gpio_ir {
+	int gpio_nr;
+	bool active_low;

Replace these 2 fields with

	struct gpio_desc *gpio;
Quoted 66 lines
drivers/media/rc/gpio-ir-tx.c new file +66 -0
+	unsigned int carrier;
+	unsigned int duty_cycle;
+	/* we need a spinlock to hold the cpu while transmitting */
+	spinlock_t lock;
+};
+
+static const struct of_device_id gpio_ir_tx_of_match[] = {
+	{ .compatible = "gpio-ir-tx", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, gpio_ir_tx_of_match);
+
+static int gpio_ir_tx_set_duty_cycle(struct rc_dev *dev, u32 duty_cycle)
+{
+	struct gpio_ir *gpio_ir = dev->priv;
+
+	gpio_ir->duty_cycle = duty_cycle;
+
+	return 0;
+}
+
+static int gpio_ir_tx_set_carrier(struct rc_dev *dev, u32 carrier)
+{
+	struct gpio_ir *gpio_ir = dev->priv;
+
+	if (!carrier)
+		return -EINVAL;
+
+	gpio_ir->carrier = carrier;
+
+	return 0;
+}
+
+static int gpio_ir_tx(struct rc_dev *dev, unsigned int *txbuf,
+		      unsigned int count)
+{
+	struct gpio_ir *gpio_ir = dev->priv;
+	unsigned long flags;
+	ktime_t edge;
+	/*
+	 * delta should never exceed 0.5 seconds (IR_MAX_DURATION) and on
+	 * m68k ndelay(s64) does not compile; so use s32 rather than s64.
+	 */
+	s32 delta;
+	int i;
+	unsigned int pulse, space;
+
+	/* Ensure the dividend fits into 32 bit */
+	pulse = DIV_ROUND_CLOSEST(gpio_ir->duty_cycle * (NSEC_PER_SEC / 100),
+				  gpio_ir->carrier);
+	space = DIV_ROUND_CLOSEST((100 - gpio_ir->duty_cycle) *
+				  (NSEC_PER_SEC / 100), gpio_ir->carrier);
+
+	spin_lock_irqsave(&gpio_ir->lock, flags);
+
+	edge = ktime_get();
+
+	for (i = 0; i < count; i++) {
+		if (i % 2) {
+			// space
+			edge = ktime_add_us(edge, txbuf[i]);
+			delta = ktime_us_delta(edge, ktime_get());
+			if (delta > 10) {
+				spin_unlock_irqrestore(&gpio_ir->lock, flags);
+				usleep_range(delta, delta + 10);
+				spin_lock_irqsave(&gpio_ir->lock, flags);

drivers/media/rc/gpio-ir-tx.c line 105

Quoted 10 lines
drivers/media/rc/gpio-ir-tx.c new file +10 -0
+			} else if (delta > 0) {
+				udelay(delta);
+			}
+		} else {
+			// pulse
+			ktime_t last = ktime_add_us(edge, txbuf[i]);
+
+			while (ktime_get() < last) {
+				gpio_set_value(gpio_ir->gpio_nr,
+					       gpio_ir->active_low);
				gpiod_set_value(gpio_ir->gpio, 1);

drivers/media/rc/gpio-ir-tx.c line 111

Quoted 6 lines
drivers/media/rc/gpio-ir-tx.c new file +6 -0
+				edge += pulse;
+				delta = edge - ktime_get();
+				if (delta > 0)
+					ndelay(delta);
+				gpio_set_value(gpio_ir->gpio_nr,
+					       !gpio_ir->active_low);
				gpiod_set_value(gpio_ir->gpio, 0);
Quoted 11 lines
drivers/media/rc/gpio-ir-tx.c new file +11 -0
+				edge += space;
+				delta = edge - ktime_get();
+				if (delta > 0)
+					ndelay(delta);
+			}
+
+			edge = last;
+		}
+	}
+
+	spin_unlock_irqrestore(&gpio_ir->lock, flags);

drivers/media/rc/gpio-ir-tx.c line 132

Quoted 10 lines
drivers/media/rc/gpio-ir-tx.c new file +10 -0
+
+	return count;
+}
+
+static int gpio_ir_tx_probe(struct platform_device *pdev)
+{
+	struct gpio_ir *gpio_ir;
+	struct rc_dev *rcdev;
+	enum of_gpio_flags flags;
+	int rc, gpio;

Flags can be dropped, gpio as well.

drivers/media/rc/gpio-ir-tx.c line 140

Quoted 8 lines
drivers/media/rc/gpio-ir-tx.c new file +8 -0
+
+	gpio = of_get_gpio_flags(pdev->dev.of_node, 0, &flags);
+	if (gpio < 0) {
+		if (gpio != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "Failed to get gpio flags (%d)\n",
+				gpio);
+		return -EINVAL;
+	}

Drop this and move getting the gpio a bit down so we don't need a temp variable.

drivers/media/rc/gpio-ir-tx.c line 148

Quoted 8 lines
drivers/media/rc/gpio-ir-tx.c new file +8 -0
+
+	gpio_ir = devm_kmalloc(&pdev->dev, sizeof(*gpio_ir), GFP_KERNEL);
+	if (!gpio_ir)
+		return -ENOMEM;
+
+	rcdev = devm_rc_allocate_device(&pdev->dev, RC_DRIVER_IR_RAW_TX);
+	if (!rcdev)
+		return -ENOMEM;

get the gpio here, configure it to output with logical low (idle) level and store it in gpio_ir:

	gpio_ir->gpio = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW);
	if (IS_ERR(gpio_ir->gpio)) {
		if (PTR_ERR(gpio_ir->gpio) != -EPROBE_DEFER)
			dev_err(&pdev->dev, "Failed to get gpio (%ld)\n",
				PTR_ERR(gpio_ir->gpio));
		return PTR_ERR(gpio_ir->gpio);
	}

drivers/media/rc/gpio-ir-tx.c line 158

Quoted 10 lines
drivers/media/rc/gpio-ir-tx.c new file +10 -0
+
+	rcdev->priv = gpio_ir;
+	rcdev->driver_name = DRIVER_NAME;
+	rcdev->device_name = DEVICE_NAME;
+	rcdev->tx_ir = gpio_ir_tx;
+	rcdev->s_tx_duty_cycle = gpio_ir_tx_set_duty_cycle;
+	rcdev->s_tx_carrier = gpio_ir_tx_set_carrier;
+
+	gpio_ir->gpio_nr = gpio;
+	gpio_ir->active_low = (flags & OF_GPIO_ACTIVE_LOW) != 0;

drop gpio_nr and active_low

drivers/media/rc/gpio-ir-tx.c line 169

Quoted 11 lines
drivers/media/rc/gpio-ir-tx.c new file +11 -0
+	gpio_ir->carrier = 38000;
+	gpio_ir->duty_cycle = 50;
+	spin_lock_init(&gpio_ir->lock);
+
+	rc = devm_gpio_request(&pdev->dev, gpio, "gpio-ir-tx");
+	if (rc < 0)
+		return rc;
+
+	rc = gpio_direction_output(gpio, !gpio_ir->active_low);
+	if (rc < 0)
+		return rc;

drop devm_gpio_request and gpio_direction_output, that is already handled by devm_gpiod_get.

Quoted 14 lines
drivers/media/rc/gpio-ir-tx.c new file +14 -0
+
+	rc = devm_rc_register_device(&pdev->dev, rcdev);
+	if (rc < 0)
+		dev_err(&pdev->dev, "failed to register rc device\n");
+
+	return rc;
+}
+
+static struct platform_driver gpio_ir_tx_driver = {
+	.probe	= gpio_ir_tx_probe,
+	.driver = {
+		.name	= DRIVER_NAME,
+		.of_match_table = of_match_ptr(gpio_ir_tx_of_match),
+	},
Quoted 8 lines
Quoted 8 lines
drivers/media/rc/gpio-ir-tx.c new file +6 -0
+};
+module_platform_driver(gpio_ir_tx_driver);
+
+MODULE_DESCRIPTION("GPIO Bit Banging IR Transmitter");
+MODULE_AUTHOR("Sean Young <[email protected]>");
+MODULE_LICENSE("GPL");
Signature · 1 line
2.9.4

I agree with your comments.

Quoted 103 lines

so long,

Hias
---

drivers/media/rc/gpio-ir-tx.c +13 -28
index 7a5371dbb360..ca6834d09467 100644
@@ -13,11 +13,10 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/of.h>
-#include <linux/of_gpio.h>
 #include <linux/platform_device.h>
 #include <media/rc-core.h>
 
@@ -25,8 +24,7 @@
 #define DEVICE_NAME	"GPIO Bit Banging IR Transmitter"
 
 struct gpio_ir {
-	int gpio_nr;
-	bool active_low;
+	struct gpio_desc *gpio;
 	unsigned int carrier;
 	unsigned int duty_cycle;
 	/* we need a spinlock to hold the cpu while transmitting */
@@ -101,14 +99,12 @@ static int gpio_ir_tx(struct rc_dev *dev, unsigned int *txbuf,
 			ktime_t last = ktime_add_us(edge, txbuf[i]);
 
 			while (ktime_get() < last) {
-				gpio_set_value(gpio_ir->gpio_nr,
-					       gpio_ir->active_low);
+				gpiod_set_value(gpio_ir->gpio, 1);
 				edge += pulse;
 				delta = edge - ktime_get();
 				if (delta > 0)
 					ndelay(delta);
-				gpio_set_value(gpio_ir->gpio_nr,
-					       !gpio_ir->active_low);
+				gpiod_set_value(gpio_ir->gpio, 0);
 				edge += space;
 				delta = edge - ktime_get();
 				if (delta > 0)
@@ -128,16 +124,7 @@ static int gpio_ir_tx_probe(struct platform_device *pdev)
 {
 	struct gpio_ir *gpio_ir;
 	struct rc_dev *rcdev;
-	enum of_gpio_flags flags;
-	int rc, gpio;
-
-	gpio = of_get_gpio_flags(pdev->dev.of_node, 0, &flags);
-	if (gpio < 0) {
-		if (gpio != -EPROBE_DEFER)
-			dev_err(&pdev->dev, "Failed to get gpio flags (%d)\n",
-				gpio);
-		return -EINVAL;
-	}
+	int rc;
 
 	gpio_ir = devm_kmalloc(&pdev->dev, sizeof(*gpio_ir), GFP_KERNEL);
 	if (!gpio_ir)
@@ -147,6 +134,14 @@ static int gpio_ir_tx_probe(struct platform_device *pdev)
 	if (!rcdev)
 		return -ENOMEM;
 
+	gpio_ir->gpio = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW);
+	if (IS_ERR(gpio_ir->gpio)) {
+		if (PTR_ERR(gpio_ir->gpio) != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "Failed to get gpio (%ld)\n",
+				PTR_ERR(gpio_ir->gpio));
+		return PTR_ERR(gpio_ir->gpio);
+	}
+
 	rcdev->priv = gpio_ir;
 	rcdev->driver_name = DRIVER_NAME;
 	rcdev->device_name = DEVICE_NAME;
@@ -154,20 +149,10 @@ static int gpio_ir_tx_probe(struct platform_device *pdev)
 	rcdev->s_tx_duty_cycle = gpio_ir_tx_set_duty_cycle;
 	rcdev->s_tx_carrier = gpio_ir_tx_set_carrier;
 
-	gpio_ir->gpio_nr = gpio;
-	gpio_ir->active_low = (flags & OF_GPIO_ACTIVE_LOW) != 0;
 	gpio_ir->carrier = 38000;
 	gpio_ir->duty_cycle = 50;
 	spin_lock_init(&gpio_ir->lock);
 
-	rc = devm_gpio_request(&pdev->dev, gpio, "gpio-ir-tx");
-	if (rc < 0)
-		return rc;
-
-	rc = gpio_direction_output(gpio, !gpio_ir->active_low);
-	if (rc < 0)
-		return rc;
-
 	rc = devm_rc_register_device(&pdev->dev, rcdev);
 	if (rc < 0)
 		dev_err(&pdev->dev, "failed to register rc device\n");
#7 lore raw

PATCH 1/1 rc: gpio-ir-tx: switch to gpiod, fix inverted polarity

Manual handling of gpio output polarity was inverted. Switch to using gpiod, this allows us to simplify the code, delegate polarity handling to gpiod and remove the buggy local polarity handling code.

---

This patch is against [PATCH v2 3/6] [media] rc: gpio-ir-tx:
add new driver.

Feel free to squash these two patches into one for v3.

so long,

Hias

 drivers/media/rc/gpio-ir-tx.c | 41 +++++++++++++----------------------------
 1 file changed, 13 insertions(+), 28 deletions(-)
drivers/media/rc/gpio-ir-tx.c +13 -28
index 7a5371dbb360..ca6834d09467 100644
@@ -13,11 +13,10 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/of.h>
-#include <linux/of_gpio.h>
 #include <linux/platform_device.h>
 #include <media/rc-core.h>
 
@@ -25,8 +24,7 @@
 #define DEVICE_NAME	"GPIO Bit Banging IR Transmitter"
 
 struct gpio_ir {
-	int gpio_nr;
-	bool active_low;
+	struct gpio_desc *gpio;
 	unsigned int carrier;
 	unsigned int duty_cycle;
 	/* we need a spinlock to hold the cpu while transmitting */
@@ -101,14 +99,12 @@ static int gpio_ir_tx(struct rc_dev *dev, unsigned int *txbuf,
 			ktime_t last = ktime_add_us(edge, txbuf[i]);
 
 			while (ktime_get() < last) {
-				gpio_set_value(gpio_ir->gpio_nr,
-					       gpio_ir->active_low);
+				gpiod_set_value(gpio_ir->gpio, 1);
 				edge += pulse;
 				delta = edge - ktime_get();
 				if (delta > 0)
 					ndelay(delta);
-				gpio_set_value(gpio_ir->gpio_nr,
-					       !gpio_ir->active_low);
+				gpiod_set_value(gpio_ir->gpio, 0);
 				edge += space;
 				delta = edge - ktime_get();
 				if (delta > 0)
@@ -128,16 +124,7 @@ static int gpio_ir_tx_probe(struct platform_device *pdev)
 {
 	struct gpio_ir *gpio_ir;
 	struct rc_dev *rcdev;
-	enum of_gpio_flags flags;
-	int rc, gpio;
-
-	gpio = of_get_gpio_flags(pdev->dev.of_node, 0, &flags);
-	if (gpio < 0) {
-		if (gpio != -EPROBE_DEFER)
-			dev_err(&pdev->dev, "Failed to get gpio flags (%d)\n",
-				gpio);
-		return -EINVAL;
-	}
+	int rc;
 
 	gpio_ir = devm_kmalloc(&pdev->dev, sizeof(*gpio_ir), GFP_KERNEL);
 	if (!gpio_ir)
@@ -147,6 +134,14 @@ static int gpio_ir_tx_probe(struct platform_device *pdev)
 	if (!rcdev)
 		return -ENOMEM;
 
+	gpio_ir->gpio = devm_gpiod_get(&pdev->dev, NULL, GPIOD_OUT_LOW);
+	if (IS_ERR(gpio_ir->gpio)) {
+		if (PTR_ERR(gpio_ir->gpio) != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "Failed to get gpio (%ld)\n",
+				PTR_ERR(gpio_ir->gpio));
+		return PTR_ERR(gpio_ir->gpio);
+	}
+
 	rcdev->priv = gpio_ir;
 	rcdev->driver_name = DRIVER_NAME;
 	rcdev->device_name = DEVICE_NAME;
@@ -154,20 +149,10 @@ static int gpio_ir_tx_probe(struct platform_device *pdev)
 	rcdev->s_tx_duty_cycle = gpio_ir_tx_set_duty_cycle;
 	rcdev->s_tx_carrier = gpio_ir_tx_set_carrier;
 
-	gpio_ir->gpio_nr = gpio;
-	gpio_ir->active_low = (flags & OF_GPIO_ACTIVE_LOW) != 0;
 	gpio_ir->carrier = 38000;
 	gpio_ir->duty_cycle = 50;
 	spin_lock_init(&gpio_ir->lock);
 
-	rc = devm_gpio_request(&pdev->dev, gpio, "gpio-ir-tx");
-	if (rc < 0)
-		return rc;
-
-	rc = gpio_direction_output(gpio, !gpio_ir->active_low);
-	if (rc < 0)
-		return rc;
-
 	rc = devm_rc_register_device(&pdev->dev, rcdev);
 	if (rc < 0)
 		dev_err(&pdev->dev, "failed to register rc device\n");
Signature · 1 line
2.11.0

A message that is not in the archive (sent off-list or not yet received): [email protected]

#9 lore raw

PATCH v2 5/6 dt-bindings: gpio-ir-tx: add support for GPIO IR Transmitter

Document the device tree bindings for the GPIO Bit Banging IR Transmitter.

---
 Documentation/devicetree/bindings/leds/irled/gpio-ir-tx.txt | 11 +++++++++++
 1 file changed, 11 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/irled/gpio-ir-tx.txt
Documentation/devicetree/bindings/leds/irled/gpio-ir-tx.txt new file +11 -0
new file mode 100644
index 0000000..bc08d89
@@ -0,0 +1,11 @@
+Device tree bindings for IR LED connected through gpio pin which is used as
+remote controller transmitter.
+
+Required properties:
+	- compatible: should be "gpio-ir-tx".
+
+Example:
+	irled@0 {
+		compatible = "gpio-ir-tx";
+		gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
+	};
Signature · 6 lines
2.9.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
maintainer
#10 lore raw

Re: [PATCH v2 5/6] [media] dt-bindings: gpio-ir-tx: add support for GPIO IR Transmitter

Quoted 15 lines · Sean Young

Document the device tree bindings for the GPIO Bit Banging IR Transmitter.

---
 Documentation/devicetree/bindings/leds/irled/gpio-ir-tx.txt | 11 +++++++++++
 1 file changed, 11 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/irled/gpio-ir-tx.txt
Documentation/devicetree/bindings/leds/irled/gpio-ir-tx.txt new file +0 -0
new file mode 100644
index 0000000..bc08d89

Documentation/devicetree/bindings/leds/irled/gpio-ir-tx.txt line 5

Quoted 5 lines
Documentation/devicetree/bindings/leds/irled/gpio-ir-tx.txt new file +5 -0
@@ -0,0 +1,11 @@
+Device tree bindings for IR LED connected through gpio pin which is used as
+remote controller transmitter.
+
+Required properties:
+	- compatible: should be "gpio-ir-tx".

As I mentioned in the prior version, missing the "gpios" property.

Quoted 13 lines
Documentation/devicetree/bindings/leds/irled/gpio-ir-tx.txt new file +6 -0
+
+Example:
+	irled@0 {
+		compatible = "gpio-ir-tx";
+		gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
+	};
Signature · 6 lines
2.9.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
#11 lore raw

Re: [PATCH v2 5/6] [media] dt-bindings: gpio-ir-tx: add support for GPIO IR Transmitter

Quoted 10 lines · Rob Herring
Quoted 9 lines · Sean Young

Document the device tree bindings for the GPIO Bit Banging IR Transmitter.

---
 Documentation/devicetree/bindings/leds/irled/gpio-ir-tx.txt | 11 +++++++++++
 1 file changed, 11 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/irled/gpio-ir-tx.txt
Quoted 13 lines

Documentation/devicetree/bindings/leds/irled/gpio-ir-tx.txt line 5

Quoted 11 lines
Documentation/devicetree/bindings/leds/irled/gpio-ir-tx.txt new file +5 -0
new file mode 100644
index 0000000..bc08d89
@@ -0,0 +1,11 @@
+Device tree bindings for IR LED connected through gpio pin which is used as
+remote controller transmitter.
+
+Required properties:
+	- compatible: should be "gpio-ir-tx".

As I mentioned in the prior version, missing the "gpios" property.

Absolutely right. I hadn't gotten round to sending out a new version yet, I'll do that soon.

Thanks,

Sean
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

A message that is not in the archive (sent off-list or not yet received): 20170710151016.5iaokchdejxozrte-3XSxi2G4b3iXFJAUJl40Xg@public.gmane.org

#13 lore raw

PATCH v3 1/1 dt-bindings: gpio-ir-tx: add support for GPIO IR Transmitter

Document the device tree bindings for the GPIO Bit Banging IR Transmitter.

---
 .../devicetree/bindings/leds/irled/gpio-ir-tx.txt          | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/irled/gpio-ir-tx.txt
Documentation/devicetree/bindings/leds/irled/gpio-ir-tx.txt new file +14 -0
new file mode 100644
index 0000000..cbe8dfd
@@ -0,0 +1,14 @@
+Device tree bindings for IR LED connected through gpio pin which is used as
+remote controller transmitter.
+
+Required properties:
+	- compatible: should be "gpio-ir-tx".
+	- gpios :  Should specify the IR LED GPIO, see "gpios property" in
+	  Documentation/devicetree/bindings/gpio/gpio.txt.  Active low LEDs
+	  should be indicated using flags in the GPIO specifier.
+
+Example:
+	irled@0 {
+		compatible = "gpio-ir-tx";
+		gpios = <&gpio1 2 GPIO_ACTIVE_HIGH>;
+	};
Signature · 6 lines
2.9.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Acked-by on v3 1/1
#14 lore raw

Re: [PATCH v3] [media] dt-bindings: gpio-ir-tx: add support for GPIO IR Transmitter

Quoted 8 lines · Sean Young

Document the device tree bindings for the GPIO Bit Banging IR Transmitter.

---
 .../devicetree/bindings/leds/irled/gpio-ir-tx.txt          | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/irled/gpio-ir-tx.txt

A message that is not in the archive (sent off-list or not yet received): 580c648de65344e9316ff153ba316efd4d527f12.1499419624.git.sean-hENCXIMQXOg@public.gmane.org

#16 lore raw

PATCH v2 6/6 dt-bindings: pwm-ir-tx: Add support for PWM IR Transmitter

Document the device tree bindings for the PWM IR Transmitter.

---
 Documentation/devicetree/bindings/leds/irled/pwm-ir-tx.txt | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/irled/pwm-ir-tx.txt
Documentation/devicetree/bindings/leds/irled/pwm-ir-tx.txt new file +13 -0
new file mode 100644
index 0000000..66e5672
@@ -0,0 +1,13 @@
+Device tree bindings for IR LED connected through pwm pin which is used as
+remote controller transmitter.
+
+Required properties:
+	- compatible: should be "pwm-ir-tx".
+	- pwms : PWM property to point to the PWM device (phandle)/port (id)
+	  and to specify the period time to be used: <&phandle id period_ns>;
+
+Example:
+	irled {
+		compatible = "pwm-ir-tx";
+		pwms = <&pwm0 0 10000000>;
+	};
Signature · 6 lines
2.9.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

A message that is not in the archive (sent off-list or not yet received): e18cec2d3f66cd59d8683cb07fc59e3a2086cf6e.1499419624.git.sean-hENCXIMQXOg@public.gmane.org

Acked-by
#18 lore raw

Re: [PATCH v2 6/6] [media] dt-bindings: pwm-ir-tx: Add support for PWM IR Transmitter

Quoted 7 lines · Sean Young

Document the device tree bindings for the PWM IR Transmitter.

---
 Documentation/devicetree/bindings/leds/irled/pwm-ir-tx.txt | 13 +++++++++++++
 1 file changed, 13 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/leds/irled/pwm-ir-tx.txt

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html

author maintainer
#19 lore raw

PATCH v2 4/6 rc: pwm-ir-tx: add new driver

In reply to Sean Young

This is new driver which uses pwm, so it is more power-efficient than the bit banging gpio-ir-tx driver.

---
 MAINTAINERS                  |   6 ++
 drivers/media/rc/Kconfig     |  12 ++++
 drivers/media/rc/Makefile    |   1 +
 drivers/media/rc/pwm-ir-tx.c | 138 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 157 insertions(+)
 create mode 100644 drivers/media/rc/pwm-ir-tx.c
MAINTAINERS +6 -0
index 8a37c2f..446a528 100644
@@ -10449,6 +10449,12 @@ F:	Documentation/devicetree/bindings/hwmon/pwm-fan.txt
 F:	Documentation/hwmon/pwm-fan
 F:	drivers/hwmon/pwm-fan.c
 
+PWM IR Transmitter
+M:	Sean Young <[email protected]>
+L:	[email protected]
+S:	Maintained
+F:	drivers/media/rc/pwm-ir-tx.c
+
 PWM SUBSYSTEM
 M:	Thierry Reding <[email protected]>
 L:	[email protected]
drivers/media/rc/Kconfig +12 -0
index ef767d1..bca77f0 100644
@@ -410,6 +410,18 @@ config IR_GPIO_TX
 	   To compile this driver as a module, choose M here: the module will
 	   be called gpio-ir-tx.
 
+config IR_PWM_TX
+	tristate "PWM IR transmitter"
+	depends on RC_CORE
+	depends on LIRC
+	depends on PWM
+	---help---
+	   Say Y if you want to use a PWM based IR transmitter. This is
+	   more power efficient than the bit banging gpio driver.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called pwm-ir-tx.
+
 config RC_ST
 	tristate "ST remote control receiver"
 	depends on RC_CORE
drivers/media/rc/Makefile +1 -0
index 3e64a4e..466c402 100644
@@ -33,6 +33,7 @@ obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o
 obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o
 obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o
 obj-$(CONFIG_IR_GPIO_TX) += gpio-ir-tx.o
+obj-$(CONFIG_IR_PWM_TX) += pwm-ir-tx.o
 obj-$(CONFIG_IR_IGORPLUGUSB) += igorplugusb.o
 obj-$(CONFIG_IR_IGUANA) += iguanair.o
 obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o
drivers/media/rc/pwm-ir-tx.c new file +138 -0

138 lines not on this page · raw message

Signature · 1 line
2.9.4
Reviewed-by on v2 4/6
#20 lore raw

Re: [PATCH v2 4/6] [media] rc: pwm-ir-tx: add new driver

Quoted 11 lines · Sean Young

This is new driver which uses pwm, so it is more power-efficient than the bit banging gpio-ir-tx driver.

---
 MAINTAINERS                  |   6 ++
 drivers/media/rc/Kconfig     |  12 ++++
 drivers/media/rc/Makefile    |   1 +
 drivers/media/rc/pwm-ir-tx.c | 138 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 157 insertions(+)
 create mode 100644 drivers/media/rc/pwm-ir-tx.c

nothing apparently wrong.

Signature · 2 lines
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
Tested-by on v2 4/6
#21 lore raw

Re: [PATCH v2 4/6] [media] rc: pwm-ir-tx: add new driver

In reply to Sean Young

Hi Sean,

sorry for double-post, forgot to CC the list and others.

Quoted 4 lines · Sean Young

This is new driver which uses pwm, so it is more power-efficient than the bit banging gpio-ir-tx driver.

Tested on RPi2, with downstream RPi kernel 4.13-rc1, using ir-ctl to send RC-5 codes, against another RPi with gpio-ir-recv. Also verified signal polarity and frequency on scope.

so long & thanks a lot,

Hias

Quoted 207 lines
---
 MAINTAINERS                  |   6 ++
 drivers/media/rc/Kconfig     |  12 ++++
 drivers/media/rc/Makefile    |   1 +
 drivers/media/rc/pwm-ir-tx.c | 138 +++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 157 insertions(+)
 create mode 100644 drivers/media/rc/pwm-ir-tx.c
MAINTAINERS +6 -0
index 8a37c2f..446a528 100644
@@ -10449,6 +10449,12 @@ F:	Documentation/devicetree/bindings/hwmon/pwm-fan.txt
 F:	Documentation/hwmon/pwm-fan
 F:	drivers/hwmon/pwm-fan.c
 
+PWM IR Transmitter
+M:	Sean Young <[email protected]>
+L:	[email protected]
+S:	Maintained
+F:	drivers/media/rc/pwm-ir-tx.c
+
 PWM SUBSYSTEM
 M:	Thierry Reding <[email protected]>
 L:	[email protected]
drivers/media/rc/Kconfig +12 -0
index ef767d1..bca77f0 100644
@@ -410,6 +410,18 @@ config IR_GPIO_TX
 	   To compile this driver as a module, choose M here: the module will
 	   be called gpio-ir-tx.
 
+config IR_PWM_TX
+	tristate "PWM IR transmitter"
+	depends on RC_CORE
+	depends on LIRC
+	depends on PWM
+	---help---
+	   Say Y if you want to use a PWM based IR transmitter. This is
+	   more power efficient than the bit banging gpio driver.
+
+	   To compile this driver as a module, choose M here: the module will
+	   be called pwm-ir-tx.
+
 config RC_ST
 	tristate "ST remote control receiver"
 	depends on RC_CORE
drivers/media/rc/Makefile +1 -0
index 3e64a4e..466c402 100644
@@ -33,6 +33,7 @@ obj-$(CONFIG_IR_WINBOND_CIR) += winbond-cir.o
 obj-$(CONFIG_RC_LOOPBACK) += rc-loopback.o
 obj-$(CONFIG_IR_GPIO_CIR) += gpio-ir-recv.o
 obj-$(CONFIG_IR_GPIO_TX) += gpio-ir-tx.o
+obj-$(CONFIG_IR_PWM_TX) += pwm-ir-tx.o
 obj-$(CONFIG_IR_IGORPLUGUSB) += igorplugusb.o
 obj-$(CONFIG_IR_IGUANA) += iguanair.o
 obj-$(CONFIG_IR_TTUSBIR) += ttusbir.o
drivers/media/rc/pwm-ir-tx.c new file +138 -0
new file mode 100644
index 0000000..27d0f58
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2017 Sean Young <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, 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/kernel.h>
+#include <linux/module.h>
+#include <linux/pwm.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <media/rc-core.h>
+
+#define DRIVER_NAME	"pwm-ir-tx"
+#define DEVICE_NAME	"PWM IR Transmitter"
+
+struct pwm_ir {
+	struct pwm_device *pwm;
+	unsigned int carrier;
+	unsigned int duty_cycle;
+};
+
+static const struct of_device_id pwm_ir_of_match[] = {
+	{ .compatible = "pwm-ir-tx", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, pwm_ir_of_match);
+
+static int pwm_ir_set_duty_cycle(struct rc_dev *dev, u32 duty_cycle)
+{
+	struct pwm_ir *pwm_ir = dev->priv;
+
+	pwm_ir->duty_cycle = duty_cycle;
+
+	return 0;
+}
+
+static int pwm_ir_set_carrier(struct rc_dev *dev, u32 carrier)
+{
+	struct pwm_ir *pwm_ir = dev->priv;
+
+	if (!carrier)
+		return -EINVAL;
+
+	pwm_ir->carrier = carrier;
+
+	return 0;
+}
+
+static int pwm_ir_tx(struct rc_dev *dev, unsigned int *txbuf,
+		     unsigned int count)
+{
+	struct pwm_ir *pwm_ir = dev->priv;
+	struct pwm_device *pwm = pwm_ir->pwm;
+	int i, duty, period;
+	ktime_t edge;
+	long delta;
+
+	period = DIV_ROUND_CLOSEST(NSEC_PER_SEC, pwm_ir->carrier);
+	duty = DIV_ROUND_CLOSEST(pwm_ir->duty_cycle * period, 100);
+
+	pwm_config(pwm, duty, period);
+
+	edge = ktime_get();
+
+	for (i = 0; i < count; i++) {
+		if (i % 2) // space
+			pwm_disable(pwm);
+		else
+			pwm_enable(pwm);
+
+		edge = ktime_add_us(edge, txbuf[i]);
+		delta = ktime_us_delta(edge, ktime_get());
+		if (delta > 0)
+			usleep_range(delta, delta + 10);
+	}
+
+	pwm_disable(pwm);
+
+	return count;
+}
+
+static int pwm_ir_probe(struct platform_device *pdev)
+{
+	struct pwm_ir *pwm_ir;
+	struct rc_dev *rcdev;
+	int rc;
+
+	pwm_ir = devm_kmalloc(&pdev->dev, sizeof(*pwm_ir), GFP_KERNEL);
+	if (!pwm_ir)
+		return -ENOMEM;
+
+	pwm_ir->pwm = devm_pwm_get(&pdev->dev, NULL);
+	if (IS_ERR(pwm_ir->pwm))
+		return PTR_ERR(pwm_ir->pwm);
+
+	pwm_ir->carrier = 38000;
+	pwm_ir->duty_cycle = 50;
+
+	rcdev = devm_rc_allocate_device(&pdev->dev, RC_DRIVER_IR_RAW_TX);
+	if (!rcdev)
+		return -ENOMEM;
+
+	rcdev->priv = pwm_ir;
+	rcdev->driver_name = DRIVER_NAME;
+	rcdev->device_name = DEVICE_NAME;
+	rcdev->tx_ir = pwm_ir_tx;
+	rcdev->s_tx_duty_cycle = pwm_ir_set_duty_cycle;
+	rcdev->s_tx_carrier = pwm_ir_set_carrier;
+
+	rc = devm_rc_register_device(&pdev->dev, rcdev);
+	if (rc < 0)
+		dev_err(&pdev->dev, "failed to register rc device\n");
+
+	return rc;
+}
+
+static struct platform_driver pwm_ir_driver = {
+	.probe = pwm_ir_probe,
+	.driver = {
+		.name	= DRIVER_NAME,
+		.of_match_table = of_match_ptr(pwm_ir_of_match),
+	},
+};
+module_platform_driver(pwm_ir_driver);
+
+MODULE_DESCRIPTION("PWM IR Transmitter");
+MODULE_AUTHOR("Sean Young <[email protected]>");
+MODULE_LICENSE("GPL");
Signature · 1 line
2.9.4