root/sound/drivers/opl4/opl4_lib.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. snd_opl4_wait
  2. snd_opl4_write
  3. snd_opl4_read
  4. snd_opl4_read_memory
  5. snd_opl4_write_memory
  6. snd_opl4_enable_opl4
  7. snd_opl4_detect
  8. snd_opl4_seq_dev_free
  9. snd_opl4_create_seq_dev
  10. snd_opl4_free
  11. snd_opl4_dev_free
  12. snd_opl4_create

   1 // SPDX-License-Identifier: GPL-2.0-or-later
   2 /*
   3  * Functions for accessing OPL4 devices
   4  * Copyright (c) 2003 by Clemens Ladisch <clemens@ladisch.de>
   5  */
   6 
   7 #include "opl4_local.h"
   8 #include <sound/initval.h>
   9 #include <linux/ioport.h>
  10 #include <linux/slab.h>
  11 #include <linux/init.h>
  12 #include <linux/module.h>
  13 #include <linux/io.h>
  14 
  15 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
  16 MODULE_DESCRIPTION("OPL4 driver");
  17 MODULE_LICENSE("GPL");
  18 
  19 static inline void snd_opl4_wait(struct snd_opl4 *opl4)
  20 {
  21         int timeout = 10;
  22         while ((inb(opl4->fm_port) & OPL4_STATUS_BUSY) && --timeout > 0)
  23                 ;
  24 }
  25 
  26 void snd_opl4_write(struct snd_opl4 *opl4, u8 reg, u8 value)
  27 {
  28         snd_opl4_wait(opl4);
  29         outb(reg, opl4->pcm_port);
  30 
  31         snd_opl4_wait(opl4);
  32         outb(value, opl4->pcm_port + 1);
  33 }
  34 
  35 EXPORT_SYMBOL(snd_opl4_write);
  36 
  37 u8 snd_opl4_read(struct snd_opl4 *opl4, u8 reg)
  38 {
  39         snd_opl4_wait(opl4);
  40         outb(reg, opl4->pcm_port);
  41 
  42         snd_opl4_wait(opl4);
  43         return inb(opl4->pcm_port + 1);
  44 }
  45 
  46 EXPORT_SYMBOL(snd_opl4_read);
  47 
  48 void snd_opl4_read_memory(struct snd_opl4 *opl4, char *buf, int offset, int size)
  49 {
  50         unsigned long flags;
  51         u8 memcfg;
  52 
  53         spin_lock_irqsave(&opl4->reg_lock, flags);
  54 
  55         memcfg = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION);
  56         snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg | OPL4_MODE_BIT);
  57 
  58         snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_HIGH, offset >> 16);
  59         snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_MID, offset >> 8);
  60         snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_LOW, offset);
  61 
  62         snd_opl4_wait(opl4);
  63         outb(OPL4_REG_MEMORY_DATA, opl4->pcm_port);
  64         snd_opl4_wait(opl4);
  65         insb(opl4->pcm_port + 1, buf, size);
  66 
  67         snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg);
  68 
  69         spin_unlock_irqrestore(&opl4->reg_lock, flags);
  70 }
  71 
  72 EXPORT_SYMBOL(snd_opl4_read_memory);
  73 
  74 void snd_opl4_write_memory(struct snd_opl4 *opl4, const char *buf, int offset, int size)
  75 {
  76         unsigned long flags;
  77         u8 memcfg;
  78 
  79         spin_lock_irqsave(&opl4->reg_lock, flags);
  80 
  81         memcfg = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION);
  82         snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg | OPL4_MODE_BIT);
  83 
  84         snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_HIGH, offset >> 16);
  85         snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_MID, offset >> 8);
  86         snd_opl4_write(opl4, OPL4_REG_MEMORY_ADDRESS_LOW, offset);
  87 
  88         snd_opl4_wait(opl4);
  89         outb(OPL4_REG_MEMORY_DATA, opl4->pcm_port);
  90         snd_opl4_wait(opl4);
  91         outsb(opl4->pcm_port + 1, buf, size);
  92 
  93         snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, memcfg);
  94 
  95         spin_unlock_irqrestore(&opl4->reg_lock, flags);
  96 }
  97 
  98 EXPORT_SYMBOL(snd_opl4_write_memory);
  99 
 100 static void snd_opl4_enable_opl4(struct snd_opl4 *opl4)
 101 {
 102         outb(OPL3_REG_MODE, opl4->fm_port + 2);
 103         inb(opl4->fm_port);
 104         inb(opl4->fm_port);
 105         outb(OPL3_OPL3_ENABLE | OPL3_OPL4_ENABLE, opl4->fm_port + 3);
 106         inb(opl4->fm_port);
 107         inb(opl4->fm_port);
 108 }
 109 
 110 static int snd_opl4_detect(struct snd_opl4 *opl4)
 111 {
 112         u8 id1, id2;
 113 
 114         snd_opl4_enable_opl4(opl4);
 115 
 116         id1 = snd_opl4_read(opl4, OPL4_REG_MEMORY_CONFIGURATION);
 117         snd_printdd("OPL4[02]=%02x\n", id1);
 118         switch (id1 & OPL4_DEVICE_ID_MASK) {
 119         case 0x20:
 120                 opl4->hardware = OPL3_HW_OPL4;
 121                 break;
 122         case 0x40:
 123                 opl4->hardware = OPL3_HW_OPL4_ML;
 124                 break;
 125         default:
 126                 return -ENODEV;
 127         }
 128 
 129         snd_opl4_write(opl4, OPL4_REG_MIX_CONTROL_FM, 0x00);
 130         snd_opl4_write(opl4, OPL4_REG_MIX_CONTROL_PCM, 0xff);
 131         id1 = snd_opl4_read(opl4, OPL4_REG_MIX_CONTROL_FM);
 132         id2 = snd_opl4_read(opl4, OPL4_REG_MIX_CONTROL_PCM);
 133         snd_printdd("OPL4 id1=%02x id2=%02x\n", id1, id2);
 134         if (id1 != 0x00 || id2 != 0xff)
 135                 return -ENODEV;
 136 
 137         snd_opl4_write(opl4, OPL4_REG_MIX_CONTROL_FM, 0x3f);
 138         snd_opl4_write(opl4, OPL4_REG_MIX_CONTROL_PCM, 0x3f);
 139         snd_opl4_write(opl4, OPL4_REG_MEMORY_CONFIGURATION, 0x00);
 140         return 0;
 141 }
 142 
 143 #if IS_ENABLED(CONFIG_SND_SEQUENCER)
 144 static void snd_opl4_seq_dev_free(struct snd_seq_device *seq_dev)
 145 {
 146         struct snd_opl4 *opl4 = seq_dev->private_data;
 147         opl4->seq_dev = NULL;
 148 }
 149 
 150 static int snd_opl4_create_seq_dev(struct snd_opl4 *opl4, int seq_device)
 151 {
 152         opl4->seq_dev_num = seq_device;
 153         if (snd_seq_device_new(opl4->card, seq_device, SNDRV_SEQ_DEV_ID_OPL4,
 154                                sizeof(struct snd_opl4 *), &opl4->seq_dev) >= 0) {
 155                 strcpy(opl4->seq_dev->name, "OPL4 Wavetable");
 156                 *(struct snd_opl4 **)SNDRV_SEQ_DEVICE_ARGPTR(opl4->seq_dev) = opl4;
 157                 opl4->seq_dev->private_data = opl4;
 158                 opl4->seq_dev->private_free = snd_opl4_seq_dev_free;
 159         }
 160         return 0;
 161 }
 162 #endif
 163 
 164 static void snd_opl4_free(struct snd_opl4 *opl4)
 165 {
 166         snd_opl4_free_proc(opl4);
 167         release_and_free_resource(opl4->res_fm_port);
 168         release_and_free_resource(opl4->res_pcm_port);
 169         kfree(opl4);
 170 }
 171 
 172 static int snd_opl4_dev_free(struct snd_device *device)
 173 {
 174         struct snd_opl4 *opl4 = device->device_data;
 175         snd_opl4_free(opl4);
 176         return 0;
 177 }
 178 
 179 int snd_opl4_create(struct snd_card *card,
 180                     unsigned long fm_port, unsigned long pcm_port,
 181                     int seq_device,
 182                     struct snd_opl3 **ropl3, struct snd_opl4 **ropl4)
 183 {
 184         struct snd_opl4 *opl4;
 185         struct snd_opl3 *opl3;
 186         int err;
 187         static struct snd_device_ops ops = {
 188                 .dev_free = snd_opl4_dev_free
 189         };
 190 
 191         if (ropl3)
 192                 *ropl3 = NULL;
 193         if (ropl4)
 194                 *ropl4 = NULL;
 195 
 196         opl4 = kzalloc(sizeof(*opl4), GFP_KERNEL);
 197         if (!opl4)
 198                 return -ENOMEM;
 199 
 200         opl4->res_fm_port = request_region(fm_port, 8, "OPL4 FM");
 201         opl4->res_pcm_port = request_region(pcm_port, 8, "OPL4 PCM/MIX");
 202         if (!opl4->res_fm_port || !opl4->res_pcm_port) {
 203                 snd_printk(KERN_ERR "opl4: can't grab ports 0x%lx, 0x%lx\n", fm_port, pcm_port);
 204                 snd_opl4_free(opl4);
 205                 return -EBUSY;
 206         }
 207 
 208         opl4->card = card;
 209         opl4->fm_port = fm_port;
 210         opl4->pcm_port = pcm_port;
 211         spin_lock_init(&opl4->reg_lock);
 212         mutex_init(&opl4->access_mutex);
 213 
 214         err = snd_opl4_detect(opl4);
 215         if (err < 0) {
 216                 snd_opl4_free(opl4);
 217                 snd_printd("OPL4 chip not detected at %#lx/%#lx\n", fm_port, pcm_port);
 218                 return err;
 219         }
 220 
 221         err = snd_device_new(card, SNDRV_DEV_CODEC, opl4, &ops);
 222         if (err < 0) {
 223                 snd_opl4_free(opl4);
 224                 return err;
 225         }
 226 
 227         err = snd_opl3_create(card, fm_port, fm_port + 2, opl4->hardware, 1, &opl3);
 228         if (err < 0) {
 229                 snd_device_free(card, opl4);
 230                 return err;
 231         }
 232 
 233         /* opl3 initialization disabled opl4, so reenable */
 234         snd_opl4_enable_opl4(opl4);
 235 
 236         snd_opl4_create_mixer(opl4);
 237         snd_opl4_create_proc(opl4);
 238 
 239 #if IS_ENABLED(CONFIG_SND_SEQUENCER)
 240         opl4->seq_client = -1;
 241         if (opl4->hardware < OPL3_HW_OPL4_ML)
 242                 snd_opl4_create_seq_dev(opl4, seq_device);
 243 #endif
 244 
 245         if (ropl3)
 246                 *ropl3 = opl3;
 247         if (ropl4)
 248                 *ropl4 = opl4;
 249         return 0;
 250 }
 251 
 252 EXPORT_SYMBOL(snd_opl4_create);

/* [<][>][^][v][top][bottom][index][help] */