162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Driver for Midiman Portman2x4 parallel port midi interface 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (c) by Levent Guendogdu <levon@feature-it.com> 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * ChangeLog 862306a36Sopenharmony_ci * Jan 24 2007 Matthias Koenig <mkoenig@suse.de> 962306a36Sopenharmony_ci * - cleanup and rewrite 1062306a36Sopenharmony_ci * Sep 30 2004 Tobias Gehrig <tobias@gehrig.tk> 1162306a36Sopenharmony_ci * - source code cleanup 1262306a36Sopenharmony_ci * Sep 03 2004 Tobias Gehrig <tobias@gehrig.tk> 1362306a36Sopenharmony_ci * - fixed compilation problem with alsa 1.0.6a (removed MODULE_CLASSES, 1462306a36Sopenharmony_ci * MODULE_PARM_SYNTAX and changed MODULE_DEVICES to 1562306a36Sopenharmony_ci * MODULE_SUPPORTED_DEVICE) 1662306a36Sopenharmony_ci * Mar 24 2004 Tobias Gehrig <tobias@gehrig.tk> 1762306a36Sopenharmony_ci * - added 2.6 kernel support 1862306a36Sopenharmony_ci * Mar 18 2004 Tobias Gehrig <tobias@gehrig.tk> 1962306a36Sopenharmony_ci * - added parport_unregister_driver to the startup routine if the driver fails to detect a portman 2062306a36Sopenharmony_ci * - added support for all 4 output ports in portman_putmidi 2162306a36Sopenharmony_ci * Mar 17 2004 Tobias Gehrig <tobias@gehrig.tk> 2262306a36Sopenharmony_ci * - added checks for opened input device in interrupt handler 2362306a36Sopenharmony_ci * Feb 20 2004 Tobias Gehrig <tobias@gehrig.tk> 2462306a36Sopenharmony_ci * - ported from alsa 0.5 to 1.0 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#include <linux/init.h> 2862306a36Sopenharmony_ci#include <linux/platform_device.h> 2962306a36Sopenharmony_ci#include <linux/parport.h> 3062306a36Sopenharmony_ci#include <linux/spinlock.h> 3162306a36Sopenharmony_ci#include <linux/delay.h> 3262306a36Sopenharmony_ci#include <linux/slab.h> 3362306a36Sopenharmony_ci#include <linux/module.h> 3462306a36Sopenharmony_ci#include <sound/core.h> 3562306a36Sopenharmony_ci#include <sound/initval.h> 3662306a36Sopenharmony_ci#include <sound/rawmidi.h> 3762306a36Sopenharmony_ci#include <sound/control.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#define CARD_NAME "Portman 2x4" 4062306a36Sopenharmony_ci#define DRIVER_NAME "portman" 4162306a36Sopenharmony_ci#define PLATFORM_DRIVER "snd_portman2x4" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; 4462306a36Sopenharmony_cistatic char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; 4562306a36Sopenharmony_cistatic bool enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic struct platform_device *platform_devices[SNDRV_CARDS]; 4862306a36Sopenharmony_cistatic int device_count; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cimodule_param_array(index, int, NULL, 0444); 5162306a36Sopenharmony_ciMODULE_PARM_DESC(index, "Index value for " CARD_NAME " soundcard."); 5262306a36Sopenharmony_cimodule_param_array(id, charp, NULL, 0444); 5362306a36Sopenharmony_ciMODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard."); 5462306a36Sopenharmony_cimodule_param_array(enable, bool, NULL, 0444); 5562306a36Sopenharmony_ciMODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ciMODULE_AUTHOR("Levent Guendogdu, Tobias Gehrig, Matthias Koenig"); 5862306a36Sopenharmony_ciMODULE_DESCRIPTION("Midiman Portman2x4"); 5962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci/********************************************************************* 6262306a36Sopenharmony_ci * Chip specific 6362306a36Sopenharmony_ci *********************************************************************/ 6462306a36Sopenharmony_ci#define PORTMAN_NUM_INPUT_PORTS 2 6562306a36Sopenharmony_ci#define PORTMAN_NUM_OUTPUT_PORTS 4 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_cistruct portman { 6862306a36Sopenharmony_ci spinlock_t reg_lock; 6962306a36Sopenharmony_ci struct snd_card *card; 7062306a36Sopenharmony_ci struct snd_rawmidi *rmidi; 7162306a36Sopenharmony_ci struct pardevice *pardev; 7262306a36Sopenharmony_ci int open_count; 7362306a36Sopenharmony_ci int mode[PORTMAN_NUM_INPUT_PORTS]; 7462306a36Sopenharmony_ci struct snd_rawmidi_substream *midi_input[PORTMAN_NUM_INPUT_PORTS]; 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_cistatic int portman_free(struct portman *pm) 7862306a36Sopenharmony_ci{ 7962306a36Sopenharmony_ci kfree(pm); 8062306a36Sopenharmony_ci return 0; 8162306a36Sopenharmony_ci} 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_cistatic int portman_create(struct snd_card *card, 8462306a36Sopenharmony_ci struct pardevice *pardev, 8562306a36Sopenharmony_ci struct portman **rchip) 8662306a36Sopenharmony_ci{ 8762306a36Sopenharmony_ci struct portman *pm; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci *rchip = NULL; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci pm = kzalloc(sizeof(struct portman), GFP_KERNEL); 9262306a36Sopenharmony_ci if (pm == NULL) 9362306a36Sopenharmony_ci return -ENOMEM; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci /* Init chip specific data */ 9662306a36Sopenharmony_ci spin_lock_init(&pm->reg_lock); 9762306a36Sopenharmony_ci pm->card = card; 9862306a36Sopenharmony_ci pm->pardev = pardev; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci *rchip = pm; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci return 0; 10362306a36Sopenharmony_ci} 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ci/********************************************************************* 10662306a36Sopenharmony_ci * HW related constants 10762306a36Sopenharmony_ci *********************************************************************/ 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci/* Standard PC parallel port status register equates. */ 11062306a36Sopenharmony_ci#define PP_STAT_BSY 0x80 /* Busy status. Inverted. */ 11162306a36Sopenharmony_ci#define PP_STAT_ACK 0x40 /* Acknowledge. Non-Inverted. */ 11262306a36Sopenharmony_ci#define PP_STAT_POUT 0x20 /* Paper Out. Non-Inverted. */ 11362306a36Sopenharmony_ci#define PP_STAT_SEL 0x10 /* Select. Non-Inverted. */ 11462306a36Sopenharmony_ci#define PP_STAT_ERR 0x08 /* Error. Non-Inverted. */ 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci/* Standard PC parallel port command register equates. */ 11762306a36Sopenharmony_ci#define PP_CMD_IEN 0x10 /* IRQ Enable. Non-Inverted. */ 11862306a36Sopenharmony_ci#define PP_CMD_SELI 0x08 /* Select Input. Inverted. */ 11962306a36Sopenharmony_ci#define PP_CMD_INIT 0x04 /* Init Printer. Non-Inverted. */ 12062306a36Sopenharmony_ci#define PP_CMD_FEED 0x02 /* Auto Feed. Inverted. */ 12162306a36Sopenharmony_ci#define PP_CMD_STB 0x01 /* Strobe. Inverted. */ 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci/* Parallel Port Command Register as implemented by PCP2x4. */ 12462306a36Sopenharmony_ci#define INT_EN PP_CMD_IEN /* Interrupt enable. */ 12562306a36Sopenharmony_ci#define STROBE PP_CMD_STB /* Command strobe. */ 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci/* The parallel port command register field (b1..b3) selects the 12862306a36Sopenharmony_ci * various "registers" within the PC/P 2x4. These are the internal 12962306a36Sopenharmony_ci * address of these "registers" that must be written to the parallel 13062306a36Sopenharmony_ci * port command register. 13162306a36Sopenharmony_ci */ 13262306a36Sopenharmony_ci#define RXDATA0 (0 << 1) /* PCP RxData channel 0. */ 13362306a36Sopenharmony_ci#define RXDATA1 (1 << 1) /* PCP RxData channel 1. */ 13462306a36Sopenharmony_ci#define GEN_CTL (2 << 1) /* PCP General Control Register. */ 13562306a36Sopenharmony_ci#define SYNC_CTL (3 << 1) /* PCP Sync Control Register. */ 13662306a36Sopenharmony_ci#define TXDATA0 (4 << 1) /* PCP TxData channel 0. */ 13762306a36Sopenharmony_ci#define TXDATA1 (5 << 1) /* PCP TxData channel 1. */ 13862306a36Sopenharmony_ci#define TXDATA2 (6 << 1) /* PCP TxData channel 2. */ 13962306a36Sopenharmony_ci#define TXDATA3 (7 << 1) /* PCP TxData channel 3. */ 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci/* Parallel Port Status Register as implemented by PCP2x4. */ 14262306a36Sopenharmony_ci#define ESTB PP_STAT_POUT /* Echoed strobe. */ 14362306a36Sopenharmony_ci#define INT_REQ PP_STAT_ACK /* Input data int request. */ 14462306a36Sopenharmony_ci#define BUSY PP_STAT_ERR /* Interface Busy. */ 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci/* Parallel Port Status Register BUSY and SELECT lines are multiplexed 14762306a36Sopenharmony_ci * between several functions. Depending on which 2x4 "register" is 14862306a36Sopenharmony_ci * currently selected (b1..b3), the BUSY and SELECT lines are 14962306a36Sopenharmony_ci * assigned as follows: 15062306a36Sopenharmony_ci * 15162306a36Sopenharmony_ci * SELECT LINE: A3 A2 A1 15262306a36Sopenharmony_ci * -------- 15362306a36Sopenharmony_ci */ 15462306a36Sopenharmony_ci#define RXAVAIL PP_STAT_SEL /* Rx Available, channel 0. 0 0 0 */ 15562306a36Sopenharmony_ci// RXAVAIL1 PP_STAT_SEL /* Rx Available, channel 1. 0 0 1 */ 15662306a36Sopenharmony_ci#define SYNC_STAT PP_STAT_SEL /* Reserved - Sync Status. 0 1 0 */ 15762306a36Sopenharmony_ci// /* Reserved. 0 1 1 */ 15862306a36Sopenharmony_ci#define TXEMPTY PP_STAT_SEL /* Tx Empty, channel 0. 1 0 0 */ 15962306a36Sopenharmony_ci// TXEMPTY1 PP_STAT_SEL /* Tx Empty, channel 1. 1 0 1 */ 16062306a36Sopenharmony_ci// TXEMPTY2 PP_STAT_SEL /* Tx Empty, channel 2. 1 1 0 */ 16162306a36Sopenharmony_ci// TXEMPTY3 PP_STAT_SEL /* Tx Empty, channel 3. 1 1 1 */ 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* BUSY LINE: A3 A2 A1 16462306a36Sopenharmony_ci * -------- 16562306a36Sopenharmony_ci */ 16662306a36Sopenharmony_ci#define RXDATA PP_STAT_BSY /* Rx Input Data, channel 0. 0 0 0 */ 16762306a36Sopenharmony_ci// RXDATA1 PP_STAT_BSY /* Rx Input Data, channel 1. 0 0 1 */ 16862306a36Sopenharmony_ci#define SYNC_DATA PP_STAT_BSY /* Reserved - Sync Data. 0 1 0 */ 16962306a36Sopenharmony_ci /* Reserved. 0 1 1 */ 17062306a36Sopenharmony_ci#define DATA_ECHO PP_STAT_BSY /* Parallel Port Data Echo. 1 0 0 */ 17162306a36Sopenharmony_ci#define A0_ECHO PP_STAT_BSY /* Address 0 Echo. 1 0 1 */ 17262306a36Sopenharmony_ci#define A1_ECHO PP_STAT_BSY /* Address 1 Echo. 1 1 0 */ 17362306a36Sopenharmony_ci#define A2_ECHO PP_STAT_BSY /* Address 2 Echo. 1 1 1 */ 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci#define PORTMAN2X4_MODE_INPUT_TRIGGERED 0x01 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci/********************************************************************* 17862306a36Sopenharmony_ci * Hardware specific functions 17962306a36Sopenharmony_ci *********************************************************************/ 18062306a36Sopenharmony_cistatic inline void portman_write_command(struct portman *pm, u8 value) 18162306a36Sopenharmony_ci{ 18262306a36Sopenharmony_ci parport_write_control(pm->pardev->port, value); 18362306a36Sopenharmony_ci} 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic inline u8 portman_read_status(struct portman *pm) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci return parport_read_status(pm->pardev->port); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic inline void portman_write_data(struct portman *pm, u8 value) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci parport_write_data(pm->pardev->port, value); 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic void portman_write_midi(struct portman *pm, 19662306a36Sopenharmony_ci int port, u8 mididata) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci int command = ((port + 4) << 1); 19962306a36Sopenharmony_ci 20062306a36Sopenharmony_ci /* Get entering data byte and port number in BL and BH respectively. 20162306a36Sopenharmony_ci * Set up Tx Channel address field for use with PP Cmd Register. 20262306a36Sopenharmony_ci * Store address field in BH register. 20362306a36Sopenharmony_ci * Inputs: AH = Output port number (0..3). 20462306a36Sopenharmony_ci * AL = Data byte. 20562306a36Sopenharmony_ci * command = TXDATA0 | INT_EN; 20662306a36Sopenharmony_ci * Align port num with address field (b1...b3), 20762306a36Sopenharmony_ci * set address for TXDatax, Strobe=0 20862306a36Sopenharmony_ci */ 20962306a36Sopenharmony_ci command |= INT_EN; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* Disable interrupts so that the process is not interrupted, then 21262306a36Sopenharmony_ci * write the address associated with the current Tx channel to the 21362306a36Sopenharmony_ci * PP Command Reg. Do not set the Strobe signal yet. 21462306a36Sopenharmony_ci */ 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci do { 21762306a36Sopenharmony_ci portman_write_command(pm, command); 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci /* While the address lines settle, write parallel output data to 22062306a36Sopenharmony_ci * PP Data Reg. This has no effect until Strobe signal is asserted. 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci portman_write_data(pm, mididata); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* If PCP channel's TxEmpty is set (TxEmpty is read through the PP 22662306a36Sopenharmony_ci * Status Register), then go write data. Else go back and wait. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_ci } while ((portman_read_status(pm) & TXEMPTY) != TXEMPTY); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci /* TxEmpty is set. Maintain PC/P destination address and assert 23162306a36Sopenharmony_ci * Strobe through the PP Command Reg. This will Strobe data into 23262306a36Sopenharmony_ci * the PC/P transmitter and set the PC/P BUSY signal. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci portman_write_command(pm, command | STROBE); 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci /* Wait for strobe line to settle and echo back through hardware. 23862306a36Sopenharmony_ci * Once it has echoed back, assume that the address and data lines 23962306a36Sopenharmony_ci * have settled! 24062306a36Sopenharmony_ci */ 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci while ((portman_read_status(pm) & ESTB) == 0) 24362306a36Sopenharmony_ci cpu_relax(); 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci /* Release strobe and immediately re-allow interrupts. */ 24662306a36Sopenharmony_ci portman_write_command(pm, command); 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci while ((portman_read_status(pm) & ESTB) == ESTB) 24962306a36Sopenharmony_ci cpu_relax(); 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci /* PC/P BUSY is now set. We must wait until BUSY resets itself. 25262306a36Sopenharmony_ci * We'll reenable ints while we're waiting. 25362306a36Sopenharmony_ci */ 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci while ((portman_read_status(pm) & BUSY) == BUSY) 25662306a36Sopenharmony_ci cpu_relax(); 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* Data sent. */ 25962306a36Sopenharmony_ci} 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci/* 26362306a36Sopenharmony_ci * Read MIDI byte from port 26462306a36Sopenharmony_ci * Attempt to read input byte from specified hardware input port (0..). 26562306a36Sopenharmony_ci * Return -1 if no data 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_cistatic int portman_read_midi(struct portman *pm, int port) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci unsigned char midi_data = 0; 27062306a36Sopenharmony_ci unsigned char cmdout; /* Saved address+IE bit. */ 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci /* Make sure clocking edge is down before starting... */ 27362306a36Sopenharmony_ci portman_write_data(pm, 0); /* Make sure edge is down. */ 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci /* Set destination address to PCP. */ 27662306a36Sopenharmony_ci cmdout = (port << 1) | INT_EN; /* Address + IE + No Strobe. */ 27762306a36Sopenharmony_ci portman_write_command(pm, cmdout); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci while ((portman_read_status(pm) & ESTB) == ESTB) 28062306a36Sopenharmony_ci cpu_relax(); /* Wait for strobe echo. */ 28162306a36Sopenharmony_ci 28262306a36Sopenharmony_ci /* After the address lines settle, check multiplexed RxAvail signal. 28362306a36Sopenharmony_ci * If data is available, read it. 28462306a36Sopenharmony_ci */ 28562306a36Sopenharmony_ci if ((portman_read_status(pm) & RXAVAIL) == 0) 28662306a36Sopenharmony_ci return -1; /* No data. */ 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_ci /* Set the Strobe signal to enable the Rx clocking circuitry. */ 28962306a36Sopenharmony_ci portman_write_command(pm, cmdout | STROBE); /* Write address+IE+Strobe. */ 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci while ((portman_read_status(pm) & ESTB) == 0) 29262306a36Sopenharmony_ci cpu_relax(); /* Wait for strobe echo. */ 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci /* The first data bit (msb) is already sitting on the input line. */ 29562306a36Sopenharmony_ci midi_data = (portman_read_status(pm) & 128); 29662306a36Sopenharmony_ci portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */ 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* Data bit 6. */ 29962306a36Sopenharmony_ci portman_write_data(pm, 0); /* Cause falling edge while data settles. */ 30062306a36Sopenharmony_ci midi_data |= (portman_read_status(pm) >> 1) & 64; 30162306a36Sopenharmony_ci portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */ 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci /* Data bit 5. */ 30462306a36Sopenharmony_ci portman_write_data(pm, 0); /* Cause falling edge while data settles. */ 30562306a36Sopenharmony_ci midi_data |= (portman_read_status(pm) >> 2) & 32; 30662306a36Sopenharmony_ci portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */ 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci /* Data bit 4. */ 30962306a36Sopenharmony_ci portman_write_data(pm, 0); /* Cause falling edge while data settles. */ 31062306a36Sopenharmony_ci midi_data |= (portman_read_status(pm) >> 3) & 16; 31162306a36Sopenharmony_ci portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */ 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_ci /* Data bit 3. */ 31462306a36Sopenharmony_ci portman_write_data(pm, 0); /* Cause falling edge while data settles. */ 31562306a36Sopenharmony_ci midi_data |= (portman_read_status(pm) >> 4) & 8; 31662306a36Sopenharmony_ci portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */ 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci /* Data bit 2. */ 31962306a36Sopenharmony_ci portman_write_data(pm, 0); /* Cause falling edge while data settles. */ 32062306a36Sopenharmony_ci midi_data |= (portman_read_status(pm) >> 5) & 4; 32162306a36Sopenharmony_ci portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */ 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* Data bit 1. */ 32462306a36Sopenharmony_ci portman_write_data(pm, 0); /* Cause falling edge while data settles. */ 32562306a36Sopenharmony_ci midi_data |= (portman_read_status(pm) >> 6) & 2; 32662306a36Sopenharmony_ci portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */ 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* Data bit 0. */ 32962306a36Sopenharmony_ci portman_write_data(pm, 0); /* Cause falling edge while data settles. */ 33062306a36Sopenharmony_ci midi_data |= (portman_read_status(pm) >> 7) & 1; 33162306a36Sopenharmony_ci portman_write_data(pm, 1); /* Cause rising edge, which shifts data. */ 33262306a36Sopenharmony_ci portman_write_data(pm, 0); /* Return data clock low. */ 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci /* De-assert Strobe and return data. */ 33662306a36Sopenharmony_ci portman_write_command(pm, cmdout); /* Output saved address+IE. */ 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci /* Wait for strobe echo. */ 33962306a36Sopenharmony_ci while ((portman_read_status(pm) & ESTB) == ESTB) 34062306a36Sopenharmony_ci cpu_relax(); 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci return (midi_data & 255); /* Shift back and return value. */ 34362306a36Sopenharmony_ci} 34462306a36Sopenharmony_ci 34562306a36Sopenharmony_ci/* 34662306a36Sopenharmony_ci * Checks if any input data on the given channel is available 34762306a36Sopenharmony_ci * Checks RxAvail 34862306a36Sopenharmony_ci */ 34962306a36Sopenharmony_cistatic int portman_data_avail(struct portman *pm, int channel) 35062306a36Sopenharmony_ci{ 35162306a36Sopenharmony_ci int command = INT_EN; 35262306a36Sopenharmony_ci switch (channel) { 35362306a36Sopenharmony_ci case 0: 35462306a36Sopenharmony_ci command |= RXDATA0; 35562306a36Sopenharmony_ci break; 35662306a36Sopenharmony_ci case 1: 35762306a36Sopenharmony_ci command |= RXDATA1; 35862306a36Sopenharmony_ci break; 35962306a36Sopenharmony_ci } 36062306a36Sopenharmony_ci /* Write hardware (assumme STROBE=0) */ 36162306a36Sopenharmony_ci portman_write_command(pm, command); 36262306a36Sopenharmony_ci /* Check multiplexed RxAvail signal */ 36362306a36Sopenharmony_ci if ((portman_read_status(pm) & RXAVAIL) == RXAVAIL) 36462306a36Sopenharmony_ci return 1; /* Data available */ 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci /* No Data available */ 36762306a36Sopenharmony_ci return 0; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci/* 37262306a36Sopenharmony_ci * Flushes any input 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_cistatic void portman_flush_input(struct portman *pm, unsigned char port) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci /* Local variable for counting things */ 37762306a36Sopenharmony_ci unsigned int i = 0; 37862306a36Sopenharmony_ci unsigned char command = 0; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci switch (port) { 38162306a36Sopenharmony_ci case 0: 38262306a36Sopenharmony_ci command = RXDATA0; 38362306a36Sopenharmony_ci break; 38462306a36Sopenharmony_ci case 1: 38562306a36Sopenharmony_ci command = RXDATA1; 38662306a36Sopenharmony_ci break; 38762306a36Sopenharmony_ci default: 38862306a36Sopenharmony_ci snd_printk(KERN_WARNING 38962306a36Sopenharmony_ci "portman_flush_input() Won't flush port %i\n", 39062306a36Sopenharmony_ci port); 39162306a36Sopenharmony_ci return; 39262306a36Sopenharmony_ci } 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci /* Set address for specified channel in port and allow to settle. */ 39562306a36Sopenharmony_ci portman_write_command(pm, command); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci /* Assert the Strobe and wait for echo back. */ 39862306a36Sopenharmony_ci portman_write_command(pm, command | STROBE); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci /* Wait for ESTB */ 40162306a36Sopenharmony_ci while ((portman_read_status(pm) & ESTB) == 0) 40262306a36Sopenharmony_ci cpu_relax(); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* Output clock cycles to the Rx circuitry. */ 40562306a36Sopenharmony_ci portman_write_data(pm, 0); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci /* Flush 250 bits... */ 40862306a36Sopenharmony_ci for (i = 0; i < 250; i++) { 40962306a36Sopenharmony_ci portman_write_data(pm, 1); 41062306a36Sopenharmony_ci portman_write_data(pm, 0); 41162306a36Sopenharmony_ci } 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci /* Deassert the Strobe signal of the port and wait for it to settle. */ 41462306a36Sopenharmony_ci portman_write_command(pm, command | INT_EN); 41562306a36Sopenharmony_ci 41662306a36Sopenharmony_ci /* Wait for settling */ 41762306a36Sopenharmony_ci while ((portman_read_status(pm) & ESTB) == ESTB) 41862306a36Sopenharmony_ci cpu_relax(); 41962306a36Sopenharmony_ci} 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_cistatic int portman_probe(struct parport *p) 42262306a36Sopenharmony_ci{ 42362306a36Sopenharmony_ci /* Initialize the parallel port data register. Will set Rx clocks 42462306a36Sopenharmony_ci * low in case we happen to be addressing the Rx ports at this time. 42562306a36Sopenharmony_ci */ 42662306a36Sopenharmony_ci /* 1 */ 42762306a36Sopenharmony_ci parport_write_data(p, 0); 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_ci /* Initialize the parallel port command register, thus initializing 43062306a36Sopenharmony_ci * hardware handshake lines to midi box: 43162306a36Sopenharmony_ci * 43262306a36Sopenharmony_ci * Strobe = 0 43362306a36Sopenharmony_ci * Interrupt Enable = 0 43462306a36Sopenharmony_ci */ 43562306a36Sopenharmony_ci /* 2 */ 43662306a36Sopenharmony_ci parport_write_control(p, 0); 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci /* Check if Portman PC/P 2x4 is out there. */ 43962306a36Sopenharmony_ci /* 3 */ 44062306a36Sopenharmony_ci parport_write_control(p, RXDATA0); /* Write Strobe=0 to command reg. */ 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci /* Check for ESTB to be clear */ 44362306a36Sopenharmony_ci /* 4 */ 44462306a36Sopenharmony_ci if ((parport_read_status(p) & ESTB) == ESTB) 44562306a36Sopenharmony_ci return 1; /* CODE 1 - Strobe Failure. */ 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci /* Set for RXDATA0 where no damage will be done. */ 44862306a36Sopenharmony_ci /* 5 */ 44962306a36Sopenharmony_ci parport_write_control(p, RXDATA0 | STROBE); /* Write Strobe=1 to command reg. */ 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci /* 6 */ 45262306a36Sopenharmony_ci if ((parport_read_status(p) & ESTB) != ESTB) 45362306a36Sopenharmony_ci return 1; /* CODE 1 - Strobe Failure. */ 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_ci /* 7 */ 45662306a36Sopenharmony_ci parport_write_control(p, 0); /* Reset Strobe=0. */ 45762306a36Sopenharmony_ci 45862306a36Sopenharmony_ci /* Check if Tx circuitry is functioning properly. If initialized 45962306a36Sopenharmony_ci * unit TxEmpty is false, send out char and see if it goes true. 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_ci /* 8 */ 46262306a36Sopenharmony_ci parport_write_control(p, TXDATA0); /* Tx channel 0, strobe off. */ 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci /* If PCP channel's TxEmpty is set (TxEmpty is read through the PP 46562306a36Sopenharmony_ci * Status Register), then go write data. Else go back and wait. 46662306a36Sopenharmony_ci */ 46762306a36Sopenharmony_ci /* 9 */ 46862306a36Sopenharmony_ci if ((parport_read_status(p) & TXEMPTY) == 0) 46962306a36Sopenharmony_ci return 2; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* Return OK status. */ 47262306a36Sopenharmony_ci return 0; 47362306a36Sopenharmony_ci} 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_cistatic int portman_device_init(struct portman *pm) 47662306a36Sopenharmony_ci{ 47762306a36Sopenharmony_ci portman_flush_input(pm, 0); 47862306a36Sopenharmony_ci portman_flush_input(pm, 1); 47962306a36Sopenharmony_ci 48062306a36Sopenharmony_ci return 0; 48162306a36Sopenharmony_ci} 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci/********************************************************************* 48462306a36Sopenharmony_ci * Rawmidi 48562306a36Sopenharmony_ci *********************************************************************/ 48662306a36Sopenharmony_cistatic int snd_portman_midi_open(struct snd_rawmidi_substream *substream) 48762306a36Sopenharmony_ci{ 48862306a36Sopenharmony_ci return 0; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic int snd_portman_midi_close(struct snd_rawmidi_substream *substream) 49262306a36Sopenharmony_ci{ 49362306a36Sopenharmony_ci return 0; 49462306a36Sopenharmony_ci} 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_cistatic void snd_portman_midi_input_trigger(struct snd_rawmidi_substream *substream, 49762306a36Sopenharmony_ci int up) 49862306a36Sopenharmony_ci{ 49962306a36Sopenharmony_ci struct portman *pm = substream->rmidi->private_data; 50062306a36Sopenharmony_ci unsigned long flags; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci spin_lock_irqsave(&pm->reg_lock, flags); 50362306a36Sopenharmony_ci if (up) 50462306a36Sopenharmony_ci pm->mode[substream->number] |= PORTMAN2X4_MODE_INPUT_TRIGGERED; 50562306a36Sopenharmony_ci else 50662306a36Sopenharmony_ci pm->mode[substream->number] &= ~PORTMAN2X4_MODE_INPUT_TRIGGERED; 50762306a36Sopenharmony_ci spin_unlock_irqrestore(&pm->reg_lock, flags); 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic void snd_portman_midi_output_trigger(struct snd_rawmidi_substream *substream, 51162306a36Sopenharmony_ci int up) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct portman *pm = substream->rmidi->private_data; 51462306a36Sopenharmony_ci unsigned long flags; 51562306a36Sopenharmony_ci unsigned char byte; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci spin_lock_irqsave(&pm->reg_lock, flags); 51862306a36Sopenharmony_ci if (up) { 51962306a36Sopenharmony_ci while ((snd_rawmidi_transmit(substream, &byte, 1) == 1)) 52062306a36Sopenharmony_ci portman_write_midi(pm, substream->number, byte); 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci spin_unlock_irqrestore(&pm->reg_lock, flags); 52362306a36Sopenharmony_ci} 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_cistatic const struct snd_rawmidi_ops snd_portman_midi_output = { 52662306a36Sopenharmony_ci .open = snd_portman_midi_open, 52762306a36Sopenharmony_ci .close = snd_portman_midi_close, 52862306a36Sopenharmony_ci .trigger = snd_portman_midi_output_trigger, 52962306a36Sopenharmony_ci}; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_cistatic const struct snd_rawmidi_ops snd_portman_midi_input = { 53262306a36Sopenharmony_ci .open = snd_portman_midi_open, 53362306a36Sopenharmony_ci .close = snd_portman_midi_close, 53462306a36Sopenharmony_ci .trigger = snd_portman_midi_input_trigger, 53562306a36Sopenharmony_ci}; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci/* Create and initialize the rawmidi component */ 53862306a36Sopenharmony_cistatic int snd_portman_rawmidi_create(struct snd_card *card) 53962306a36Sopenharmony_ci{ 54062306a36Sopenharmony_ci struct portman *pm = card->private_data; 54162306a36Sopenharmony_ci struct snd_rawmidi *rmidi; 54262306a36Sopenharmony_ci struct snd_rawmidi_substream *substream; 54362306a36Sopenharmony_ci int err; 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci err = snd_rawmidi_new(card, CARD_NAME, 0, 54662306a36Sopenharmony_ci PORTMAN_NUM_OUTPUT_PORTS, 54762306a36Sopenharmony_ci PORTMAN_NUM_INPUT_PORTS, 54862306a36Sopenharmony_ci &rmidi); 54962306a36Sopenharmony_ci if (err < 0) 55062306a36Sopenharmony_ci return err; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci rmidi->private_data = pm; 55362306a36Sopenharmony_ci strcpy(rmidi->name, CARD_NAME); 55462306a36Sopenharmony_ci rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT | 55562306a36Sopenharmony_ci SNDRV_RAWMIDI_INFO_INPUT | 55662306a36Sopenharmony_ci SNDRV_RAWMIDI_INFO_DUPLEX; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci pm->rmidi = rmidi; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci /* register rawmidi ops */ 56162306a36Sopenharmony_ci snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, 56262306a36Sopenharmony_ci &snd_portman_midi_output); 56362306a36Sopenharmony_ci snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, 56462306a36Sopenharmony_ci &snd_portman_midi_input); 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci /* name substreams */ 56762306a36Sopenharmony_ci /* output */ 56862306a36Sopenharmony_ci list_for_each_entry(substream, 56962306a36Sopenharmony_ci &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams, 57062306a36Sopenharmony_ci list) { 57162306a36Sopenharmony_ci sprintf(substream->name, 57262306a36Sopenharmony_ci "Portman2x4 %d", substream->number+1); 57362306a36Sopenharmony_ci } 57462306a36Sopenharmony_ci /* input */ 57562306a36Sopenharmony_ci list_for_each_entry(substream, 57662306a36Sopenharmony_ci &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams, 57762306a36Sopenharmony_ci list) { 57862306a36Sopenharmony_ci pm->midi_input[substream->number] = substream; 57962306a36Sopenharmony_ci sprintf(substream->name, 58062306a36Sopenharmony_ci "Portman2x4 %d", substream->number+1); 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci return err; 58462306a36Sopenharmony_ci} 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci/********************************************************************* 58762306a36Sopenharmony_ci * parport stuff 58862306a36Sopenharmony_ci *********************************************************************/ 58962306a36Sopenharmony_cistatic void snd_portman_interrupt(void *userdata) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci unsigned char midivalue = 0; 59262306a36Sopenharmony_ci struct portman *pm = ((struct snd_card*)userdata)->private_data; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci spin_lock(&pm->reg_lock); 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci /* While any input data is waiting */ 59762306a36Sopenharmony_ci while ((portman_read_status(pm) & INT_REQ) == INT_REQ) { 59862306a36Sopenharmony_ci /* If data available on channel 0, 59962306a36Sopenharmony_ci read it and stuff it into the queue. */ 60062306a36Sopenharmony_ci if (portman_data_avail(pm, 0)) { 60162306a36Sopenharmony_ci /* Read Midi */ 60262306a36Sopenharmony_ci midivalue = portman_read_midi(pm, 0); 60362306a36Sopenharmony_ci /* put midi into queue... */ 60462306a36Sopenharmony_ci if (pm->mode[0] & PORTMAN2X4_MODE_INPUT_TRIGGERED) 60562306a36Sopenharmony_ci snd_rawmidi_receive(pm->midi_input[0], 60662306a36Sopenharmony_ci &midivalue, 1); 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci /* If data available on channel 1, 61062306a36Sopenharmony_ci read it and stuff it into the queue. */ 61162306a36Sopenharmony_ci if (portman_data_avail(pm, 1)) { 61262306a36Sopenharmony_ci /* Read Midi */ 61362306a36Sopenharmony_ci midivalue = portman_read_midi(pm, 1); 61462306a36Sopenharmony_ci /* put midi into queue... */ 61562306a36Sopenharmony_ci if (pm->mode[1] & PORTMAN2X4_MODE_INPUT_TRIGGERED) 61662306a36Sopenharmony_ci snd_rawmidi_receive(pm->midi_input[1], 61762306a36Sopenharmony_ci &midivalue, 1); 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci } 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci spin_unlock(&pm->reg_lock); 62362306a36Sopenharmony_ci} 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_cistatic void snd_portman_attach(struct parport *p) 62662306a36Sopenharmony_ci{ 62762306a36Sopenharmony_ci struct platform_device *device; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci device = platform_device_alloc(PLATFORM_DRIVER, device_count); 63062306a36Sopenharmony_ci if (!device) 63162306a36Sopenharmony_ci return; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci /* Temporary assignment to forward the parport */ 63462306a36Sopenharmony_ci platform_set_drvdata(device, p); 63562306a36Sopenharmony_ci 63662306a36Sopenharmony_ci if (platform_device_add(device) < 0) { 63762306a36Sopenharmony_ci platform_device_put(device); 63862306a36Sopenharmony_ci return; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* Since we dont get the return value of probe 64262306a36Sopenharmony_ci * We need to check if device probing succeeded or not */ 64362306a36Sopenharmony_ci if (!platform_get_drvdata(device)) { 64462306a36Sopenharmony_ci platform_device_unregister(device); 64562306a36Sopenharmony_ci return; 64662306a36Sopenharmony_ci } 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci /* register device in global table */ 64962306a36Sopenharmony_ci platform_devices[device_count] = device; 65062306a36Sopenharmony_ci device_count++; 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_cistatic void snd_portman_detach(struct parport *p) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci /* nothing to do here */ 65662306a36Sopenharmony_ci} 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_cistatic int snd_portman_dev_probe(struct pardevice *pardev) 65962306a36Sopenharmony_ci{ 66062306a36Sopenharmony_ci if (strcmp(pardev->name, DRIVER_NAME)) 66162306a36Sopenharmony_ci return -ENODEV; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci return 0; 66462306a36Sopenharmony_ci} 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_cistatic struct parport_driver portman_parport_driver = { 66762306a36Sopenharmony_ci .name = "portman2x4", 66862306a36Sopenharmony_ci .probe = snd_portman_dev_probe, 66962306a36Sopenharmony_ci .match_port = snd_portman_attach, 67062306a36Sopenharmony_ci .detach = snd_portman_detach, 67162306a36Sopenharmony_ci .devmodel = true, 67262306a36Sopenharmony_ci}; 67362306a36Sopenharmony_ci 67462306a36Sopenharmony_ci/********************************************************************* 67562306a36Sopenharmony_ci * platform stuff 67662306a36Sopenharmony_ci *********************************************************************/ 67762306a36Sopenharmony_cistatic void snd_portman_card_private_free(struct snd_card *card) 67862306a36Sopenharmony_ci{ 67962306a36Sopenharmony_ci struct portman *pm = card->private_data; 68062306a36Sopenharmony_ci struct pardevice *pardev = pm->pardev; 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_ci if (pardev) { 68362306a36Sopenharmony_ci parport_release(pardev); 68462306a36Sopenharmony_ci parport_unregister_device(pardev); 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci portman_free(pm); 68862306a36Sopenharmony_ci} 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_cistatic int snd_portman_probe(struct platform_device *pdev) 69162306a36Sopenharmony_ci{ 69262306a36Sopenharmony_ci struct pardevice *pardev; 69362306a36Sopenharmony_ci struct parport *p; 69462306a36Sopenharmony_ci int dev = pdev->id; 69562306a36Sopenharmony_ci struct snd_card *card = NULL; 69662306a36Sopenharmony_ci struct portman *pm = NULL; 69762306a36Sopenharmony_ci int err; 69862306a36Sopenharmony_ci struct pardev_cb portman_cb = { 69962306a36Sopenharmony_ci .preempt = NULL, 70062306a36Sopenharmony_ci .wakeup = NULL, 70162306a36Sopenharmony_ci .irq_func = snd_portman_interrupt, /* ISR */ 70262306a36Sopenharmony_ci .flags = PARPORT_DEV_EXCL, /* flags */ 70362306a36Sopenharmony_ci }; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci p = platform_get_drvdata(pdev); 70662306a36Sopenharmony_ci platform_set_drvdata(pdev, NULL); 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci if (dev >= SNDRV_CARDS) 70962306a36Sopenharmony_ci return -ENODEV; 71062306a36Sopenharmony_ci if (!enable[dev]) 71162306a36Sopenharmony_ci return -ENOENT; 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_ci err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE, 71462306a36Sopenharmony_ci 0, &card); 71562306a36Sopenharmony_ci if (err < 0) { 71662306a36Sopenharmony_ci snd_printd("Cannot create card\n"); 71762306a36Sopenharmony_ci return err; 71862306a36Sopenharmony_ci } 71962306a36Sopenharmony_ci strcpy(card->driver, DRIVER_NAME); 72062306a36Sopenharmony_ci strcpy(card->shortname, CARD_NAME); 72162306a36Sopenharmony_ci sprintf(card->longname, "%s at 0x%lx, irq %i", 72262306a36Sopenharmony_ci card->shortname, p->base, p->irq); 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci portman_cb.private = card; /* private */ 72562306a36Sopenharmony_ci pardev = parport_register_dev_model(p, /* port */ 72662306a36Sopenharmony_ci DRIVER_NAME, /* name */ 72762306a36Sopenharmony_ci &portman_cb, /* callbacks */ 72862306a36Sopenharmony_ci pdev->id); /* device number */ 72962306a36Sopenharmony_ci if (pardev == NULL) { 73062306a36Sopenharmony_ci snd_printd("Cannot register pardevice\n"); 73162306a36Sopenharmony_ci err = -EIO; 73262306a36Sopenharmony_ci goto __err; 73362306a36Sopenharmony_ci } 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci /* claim parport */ 73662306a36Sopenharmony_ci if (parport_claim(pardev)) { 73762306a36Sopenharmony_ci snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base); 73862306a36Sopenharmony_ci err = -EIO; 73962306a36Sopenharmony_ci goto free_pardev; 74062306a36Sopenharmony_ci } 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci err = portman_create(card, pardev, &pm); 74362306a36Sopenharmony_ci if (err < 0) { 74462306a36Sopenharmony_ci snd_printd("Cannot create main component\n"); 74562306a36Sopenharmony_ci goto release_pardev; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci card->private_data = pm; 74862306a36Sopenharmony_ci card->private_free = snd_portman_card_private_free; 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci err = portman_probe(p); 75162306a36Sopenharmony_ci if (err) { 75262306a36Sopenharmony_ci err = -EIO; 75362306a36Sopenharmony_ci goto __err; 75462306a36Sopenharmony_ci } 75562306a36Sopenharmony_ci 75662306a36Sopenharmony_ci err = snd_portman_rawmidi_create(card); 75762306a36Sopenharmony_ci if (err < 0) { 75862306a36Sopenharmony_ci snd_printd("Creating Rawmidi component failed\n"); 75962306a36Sopenharmony_ci goto __err; 76062306a36Sopenharmony_ci } 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci /* init device */ 76362306a36Sopenharmony_ci err = portman_device_init(pm); 76462306a36Sopenharmony_ci if (err < 0) 76562306a36Sopenharmony_ci goto __err; 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_ci platform_set_drvdata(pdev, card); 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci /* At this point card will be usable */ 77062306a36Sopenharmony_ci err = snd_card_register(card); 77162306a36Sopenharmony_ci if (err < 0) { 77262306a36Sopenharmony_ci snd_printd("Cannot register card\n"); 77362306a36Sopenharmony_ci goto __err; 77462306a36Sopenharmony_ci } 77562306a36Sopenharmony_ci 77662306a36Sopenharmony_ci snd_printk(KERN_INFO "Portman 2x4 on 0x%lx\n", p->base); 77762306a36Sopenharmony_ci return 0; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_cirelease_pardev: 78062306a36Sopenharmony_ci parport_release(pardev); 78162306a36Sopenharmony_cifree_pardev: 78262306a36Sopenharmony_ci parport_unregister_device(pardev); 78362306a36Sopenharmony_ci__err: 78462306a36Sopenharmony_ci snd_card_free(card); 78562306a36Sopenharmony_ci return err; 78662306a36Sopenharmony_ci} 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_cistatic void snd_portman_remove(struct platform_device *pdev) 78962306a36Sopenharmony_ci{ 79062306a36Sopenharmony_ci struct snd_card *card = platform_get_drvdata(pdev); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci if (card) 79362306a36Sopenharmony_ci snd_card_free(card); 79462306a36Sopenharmony_ci} 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_cistatic struct platform_driver snd_portman_driver = { 79862306a36Sopenharmony_ci .probe = snd_portman_probe, 79962306a36Sopenharmony_ci .remove_new = snd_portman_remove, 80062306a36Sopenharmony_ci .driver = { 80162306a36Sopenharmony_ci .name = PLATFORM_DRIVER, 80262306a36Sopenharmony_ci } 80362306a36Sopenharmony_ci}; 80462306a36Sopenharmony_ci 80562306a36Sopenharmony_ci/********************************************************************* 80662306a36Sopenharmony_ci * module init stuff 80762306a36Sopenharmony_ci *********************************************************************/ 80862306a36Sopenharmony_cistatic void snd_portman_unregister_all(void) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci int i; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci for (i = 0; i < SNDRV_CARDS; ++i) { 81362306a36Sopenharmony_ci if (platform_devices[i]) { 81462306a36Sopenharmony_ci platform_device_unregister(platform_devices[i]); 81562306a36Sopenharmony_ci platform_devices[i] = NULL; 81662306a36Sopenharmony_ci } 81762306a36Sopenharmony_ci } 81862306a36Sopenharmony_ci platform_driver_unregister(&snd_portman_driver); 81962306a36Sopenharmony_ci parport_unregister_driver(&portman_parport_driver); 82062306a36Sopenharmony_ci} 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_cistatic int __init snd_portman_module_init(void) 82362306a36Sopenharmony_ci{ 82462306a36Sopenharmony_ci int err; 82562306a36Sopenharmony_ci 82662306a36Sopenharmony_ci err = platform_driver_register(&snd_portman_driver); 82762306a36Sopenharmony_ci if (err < 0) 82862306a36Sopenharmony_ci return err; 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_ci if (parport_register_driver(&portman_parport_driver) != 0) { 83162306a36Sopenharmony_ci platform_driver_unregister(&snd_portman_driver); 83262306a36Sopenharmony_ci return -EIO; 83362306a36Sopenharmony_ci } 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci if (device_count == 0) { 83662306a36Sopenharmony_ci snd_portman_unregister_all(); 83762306a36Sopenharmony_ci return -ENODEV; 83862306a36Sopenharmony_ci } 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci return 0; 84162306a36Sopenharmony_ci} 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_cistatic void __exit snd_portman_module_exit(void) 84462306a36Sopenharmony_ci{ 84562306a36Sopenharmony_ci snd_portman_unregister_all(); 84662306a36Sopenharmony_ci} 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_cimodule_init(snd_portman_module_init); 84962306a36Sopenharmony_cimodule_exit(snd_portman_module_exit); 850