From patchwork Wed Nov 29 11:08:26 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ladislav Michl X-Patchwork-Id: 10081883 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 46CFD60353 for ; Wed, 29 Nov 2017 11:08:31 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 41B6C295DC for ; Wed, 29 Nov 2017 11:08:31 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 360AC2974A; Wed, 29 Nov 2017 11:08:31 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 86ECB295DC for ; Wed, 29 Nov 2017 11:08:30 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752682AbdK2LI3 (ORCPT ); Wed, 29 Nov 2017 06:08:29 -0500 Received: from eddie.linux-mips.org ([148.251.95.138]:55266 "EHLO cvs.linux-mips.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752604AbdK2LI3 (ORCPT ); Wed, 29 Nov 2017 06:08:29 -0500 Received: (from localhost user: 'ladis' uid#1021 fake: STDIN (ladis@eddie.linux-mips.org)) by eddie.linux-mips.org id S23990477AbdK2LI1kIe4I (ORCPT ); Wed, 29 Nov 2017 12:08:27 +0100 Date: Wed, 29 Nov 2017 12:08:26 +0100 From: Ladislav Michl To: linux-input@vger.kernel.org Cc: Dmitry Torokhov Subject: [PATCH 2/2] Input: gpio-beeper: drive beeper pin by hrtimer Message-ID: <20171129110825.GC24064@lenoch> References: <20171129110627.GA24064@lenoch> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <20171129110627.GA24064@lenoch> User-Agent: Mutt/1.9.1 (2017-09-22) Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP This is poor man's solution for those who cannot use pwm-beeper. Beeper pin is driven by hrtimer, so hearable jitter is expected, but should be acceptable for frequencies under 1kHz. This mode is enabled by adding 'beeper-hz' node property. Signed-off-by: Ladislav Michl --- .../devicetree/bindings/input/gpio-beeper.txt | 5 ++ drivers/input/misc/gpio-beeper.c | 92 +++++++++++++++++++--- 2 files changed, 87 insertions(+), 10 deletions(-) diff --git a/Documentation/devicetree/bindings/input/gpio-beeper.txt b/Documentation/devicetree/bindings/input/gpio-beeper.txt index a5086e37fce6..855f8cf8af10 100644 --- a/Documentation/devicetree/bindings/input/gpio-beeper.txt +++ b/Documentation/devicetree/bindings/input/gpio-beeper.txt @@ -6,6 +6,11 @@ Required properties: - compatible: Should be "gpio-beeper". - gpios: From common gpio binding; gpio connection to beeper enable pin. +Optional properties: +- beeper-hz: Bell frequency in Hz. This option enables hrtimer driven beeper + pin toggle. This is only good for poorly designed hardware + where PWM cannot be used as there always be hearable jitter. + Example: beeper: beeper { compatible = "gpio-beeper"; diff --git a/drivers/input/misc/gpio-beeper.c b/drivers/input/misc/gpio-beeper.c index 409c85da71c3..37f22884e967 100644 --- a/drivers/input/misc/gpio-beeper.c +++ b/drivers/input/misc/gpio-beeper.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -20,11 +21,19 @@ struct gpio_beeper { struct work_struct work; + struct hrtimer timer; struct gpio_desc *desc; - bool beeping; + ktime_t tick; + unsigned int bell_freq; + int on; }; -static void gpio_beeper_toggle(struct gpio_beeper *beep, bool on) +static void gpio_beeper_toggle(struct gpio_beeper *beep, int on) +{ + gpiod_set_value(beep->desc, on); +} + +static void gpio_beeper_toggle_cansleep(struct gpio_beeper *beep, int on) { gpiod_set_value_cansleep(beep->desc, on); } @@ -33,7 +42,17 @@ static void gpio_beeper_work(struct work_struct *work) { struct gpio_beeper *beep = container_of(work, struct gpio_beeper, work); - gpio_beeper_toggle(beep, beep->beeping); + gpio_beeper_toggle_cansleep(beep, beep->on); +} + +static enum hrtimer_restart gpio_beeper_timer(struct hrtimer *timer) +{ + struct gpio_beeper *beep = container_of(timer, struct gpio_beeper, timer); + + beep->on = !beep->on; + gpio_beeper_toggle(beep, beep->on); + hrtimer_forward_now(timer, beep->tick); + return HRTIMER_RESTART; } static int gpio_beeper_event(struct input_dev *dev, unsigned int type, @@ -47,19 +66,59 @@ static int gpio_beeper_event(struct input_dev *dev, unsigned int type, if (value < 0) return -EINVAL; - beep->beeping = value; + beep->on = value; /* Schedule work to actually turn the beeper on or off */ schedule_work(&beep->work); return 0; } +static int gpio_beeper_tone_event(struct input_dev *dev, unsigned int type, + unsigned int code, int value) +{ + struct gpio_beeper *beep = input_get_drvdata(dev); + + if (type != EV_SND) + return -ENOTSUPP; + + switch (code) { + case SND_BELL: + value = value ? beep->bell_freq : 0; + break; + case SND_TONE: + break; + default: + return -ENOTSUPP; + } + + if (value < 0) + return -EINVAL; + + if (value) { + beep->tick = ns_to_ktime(1000000000UL / 2 / value); + hrtimer_start(&beep->timer, ns_to_ktime(0), HRTIMER_MODE_REL); + } else { + hrtimer_cancel(&beep->timer); + gpio_beeper_toggle(beep, 0); + } + + return 0; +} + static void gpio_beeper_close(struct input_dev *input) { struct gpio_beeper *beep = input_get_drvdata(input); cancel_work_sync(&beep->work); - gpio_beeper_toggle(beep, false); + gpio_beeper_toggle_cansleep(beep, 0); +} + +static void gpio_beeper_tone_close(struct input_dev *input) +{ + struct gpio_beeper *beep = input_get_drvdata(input); + + hrtimer_cancel(&beep->timer); + gpio_beeper_toggle_cansleep(beep, 0); } static int gpio_beeper_probe(struct platform_device *pdev) @@ -80,18 +139,31 @@ static int gpio_beeper_probe(struct platform_device *pdev) if (!input) return -ENOMEM; - INIT_WORK(&beep->work, gpio_beeper_work); - input->name = pdev->name; input->id.bustype = BUS_HOST; input->id.vendor = 0x0001; input->id.product = 0x0001; input->id.version = 0x0100; - input->close = gpio_beeper_close; - input->event = gpio_beeper_event; - input_set_capability(input, EV_SND, SND_BELL); + if (device_property_read_u32(dev, "beeper-hz", &beep->bell_freq)) { + input->close = gpio_beeper_close; + input->event = gpio_beeper_event; + + INIT_WORK(&beep->work, gpio_beeper_work); + } else { + dev_dbg(dev, + "tone mode enabled using default frequency: %uHz\n", + beep->bell_freq); + + input->close = gpio_beeper_tone_close; + input->event = gpio_beeper_tone_event; + input_set_capability(input, EV_SND, SND_TONE); + + hrtimer_init(&beep->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + beep->timer.function = gpio_beeper_timer; + } + input_set_drvdata(input, beep); return input_register_device(input);