162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * PC Speaker beeper driver for Linux 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) 2002 Vojtech Pavlik 662306a36Sopenharmony_ci * Copyright (c) 1992 Orest Zborowski 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci#include <linux/init.h> 1162306a36Sopenharmony_ci#include <linux/input.h> 1262306a36Sopenharmony_ci#include <linux/io.h> 1362306a36Sopenharmony_ci#include "pcsp.h" 1462306a36Sopenharmony_ci#include "pcsp_input.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistatic void pcspkr_do_sound(unsigned int count) 1762306a36Sopenharmony_ci{ 1862306a36Sopenharmony_ci unsigned long flags; 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci raw_spin_lock_irqsave(&i8253_lock, flags); 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci if (count) { 2362306a36Sopenharmony_ci /* set command for counter 2, 2 byte write */ 2462306a36Sopenharmony_ci outb_p(0xB6, 0x43); 2562306a36Sopenharmony_ci /* select desired HZ */ 2662306a36Sopenharmony_ci outb_p(count & 0xff, 0x42); 2762306a36Sopenharmony_ci outb((count >> 8) & 0xff, 0x42); 2862306a36Sopenharmony_ci /* enable counter 2 */ 2962306a36Sopenharmony_ci outb_p(inb_p(0x61) | 3, 0x61); 3062306a36Sopenharmony_ci } else { 3162306a36Sopenharmony_ci /* disable counter 2 */ 3262306a36Sopenharmony_ci outb(inb_p(0x61) & 0xFC, 0x61); 3362306a36Sopenharmony_ci } 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci raw_spin_unlock_irqrestore(&i8253_lock, flags); 3662306a36Sopenharmony_ci} 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_civoid pcspkr_stop_sound(void) 3962306a36Sopenharmony_ci{ 4062306a36Sopenharmony_ci pcspkr_do_sound(0); 4162306a36Sopenharmony_ci} 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic int pcspkr_input_event(struct input_dev *dev, unsigned int type, 4462306a36Sopenharmony_ci unsigned int code, int value) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci unsigned int count = 0; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if (atomic_read(&pcsp_chip.timer_active) || !pcsp_chip.pcspkr) 4962306a36Sopenharmony_ci return 0; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci switch (type) { 5262306a36Sopenharmony_ci case EV_SND: 5362306a36Sopenharmony_ci switch (code) { 5462306a36Sopenharmony_ci case SND_BELL: 5562306a36Sopenharmony_ci if (value) 5662306a36Sopenharmony_ci value = 1000; 5762306a36Sopenharmony_ci break; 5862306a36Sopenharmony_ci case SND_TONE: 5962306a36Sopenharmony_ci break; 6062306a36Sopenharmony_ci default: 6162306a36Sopenharmony_ci return -1; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci break; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci default: 6662306a36Sopenharmony_ci return -1; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci if (value > 20 && value < 32767) 7062306a36Sopenharmony_ci count = PIT_TICK_RATE / value; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci pcspkr_do_sound(count); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci return 0; 7562306a36Sopenharmony_ci} 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ciint pcspkr_input_init(struct input_dev **rdev, struct device *dev) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci int err; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci struct input_dev *input_dev = devm_input_allocate_device(dev); 8262306a36Sopenharmony_ci if (!input_dev) 8362306a36Sopenharmony_ci return -ENOMEM; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci input_dev->name = "PC Speaker"; 8662306a36Sopenharmony_ci input_dev->phys = "isa0061/input0"; 8762306a36Sopenharmony_ci input_dev->id.bustype = BUS_ISA; 8862306a36Sopenharmony_ci input_dev->id.vendor = 0x001f; 8962306a36Sopenharmony_ci input_dev->id.product = 0x0001; 9062306a36Sopenharmony_ci input_dev->id.version = 0x0100; 9162306a36Sopenharmony_ci input_dev->dev.parent = dev; 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci input_dev->evbit[0] = BIT(EV_SND); 9462306a36Sopenharmony_ci input_dev->sndbit[0] = BIT(SND_BELL) | BIT(SND_TONE); 9562306a36Sopenharmony_ci input_dev->event = pcspkr_input_event; 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci err = input_register_device(input_dev); 9862306a36Sopenharmony_ci if (err) 9962306a36Sopenharmony_ci return err; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci *rdev = input_dev; 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 104