1 /* 2 Copyright 1999, Be Incorporated. All Rights Reserved. 3 This file may be used under the terms of the Be Sample Code License. 4 */ 5 6 #include "cm_private.h" 7 #include <string.h> 8 9 #if !defined(_KERNEL_EXPORT_H) 10 #include <KernelExport.h> 11 #endif /* _KERNEL_EXPORT_H */ 12 13 14 static status_t mixer_open(const char *name, uint32 flags, void **cookie); 15 static status_t mixer_close(void *cookie); 16 static status_t mixer_free(void *cookie); 17 static status_t mixer_control(void *cookie, uint32 op, void *data, size_t len); 18 static status_t mixer_read(void *cookie, off_t pos, void *data, size_t *len); 19 static status_t mixer_write(void *cookie, off_t pos, const void *data, size_t *len); 20 21 device_hooks mixer_hooks = { 22 &mixer_open, 23 &mixer_close, 24 &mixer_free, 25 &mixer_control, 26 &mixer_read, 27 &mixer_write, 28 NULL, /* select */ 29 NULL, /* deselect */ 30 NULL, /* readv */ 31 NULL /* writev */ 32 }; 33 34 35 typedef struct { 36 int selector; 37 int port; 38 float div; 39 float sub; 40 int minval; 41 int maxval; 42 int leftshift; 43 int mask; 44 int mutemask; 45 } mixer_info; 46 47 /* mute is special -- when it's 0x01, it means enable... */ 48 mixer_info the_mixers[] = { 49 {CMEDIA_PCI_LEFT_ADC_INPUT_G, 0, 1.5, 0.0, 0, 15, 0, 0x0f, 0x00}, 50 {CMEDIA_PCI_RIGHT_ADC_INPUT_G, 1, 1.5, 0.0, 0, 15, 0, 0x0f, 0x00}, 51 {CMEDIA_PCI_LEFT_AUX1_LOOPBACK_GAM, 2, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, 52 {CMEDIA_PCI_RIGHT_AUX1_LOOPBACK_GAM, 3, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, 53 {CMEDIA_PCI_LEFT_CD_LOOPBACK_GAM, 4, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, 54 {CMEDIA_PCI_RIGHT_CD_LOOPBACK_GAM, 5, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, 55 {CMEDIA_PCI_LEFT_LINE_LOOPBACK_GAM, 6, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, 56 {CMEDIA_PCI_RIGHT_LINE_LOOPBACK_GAM, 7, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, 57 {CMEDIA_PCI_MIC_LOOPBACK_GAM, 8, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, 58 {CMEDIA_PCI_LEFT_SYNTH_OUTPUT_GAM, 0xa, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, 59 {CMEDIA_PCI_RIGHT_SYNTH_OUTPUT_GAM, 0xb, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, 60 {CMEDIA_PCI_LEFT_AUX2_LOOPBACK_GAM, 0xc, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, 61 {CMEDIA_PCI_RIGHT_AUX2_LOOPBACK_GAM, 0xd, -1.5, 12.0, 0, 31, 0, 0x1f, 0x80}, 62 {CMEDIA_PCI_LEFT_MASTER_VOLUME_AM, 0xe, -1.5, 0.0, 0, 31, 0, 0x1f, 0x80}, 63 {CMEDIA_PCI_RIGHT_MASTER_VOLUME_AM, 0xf, -1.5, 0.0, 0, 31, 0, 0x1f, 0x80}, 64 {CMEDIA_PCI_LEFT_PCM_OUTPUT_GAM, 0x10, -1.5, 0.0, 0, 63, 0, 0x3f, 0x80}, 65 {CMEDIA_PCI_RIGHT_PCM_OUTPUT_GAM, 0x11, -1.5, 0.0, 0, 63, 0, 0x3f, 0x80}, 66 {CMEDIA_PCI_DIGITAL_LOOPBACK_AM, 0x16, -1.5, 0.0, 0, 63, 2, 0xfc, 0x01}, 67 }; 68 #define N_MIXERS (sizeof(the_mixers) / sizeof(the_mixers[0])) 69 70 static int 71 map_mixer(int selector) { 72 int i; 73 for (i = 0; i < N_MIXERS; i++) 74 if (the_mixers[i].selector == selector) 75 return i; 76 return -1; 77 } 78 79 80 static status_t 81 mixer_open( 82 const char * name, 83 uint32 flags, 84 void ** cookie) 85 { 86 int ix; 87 /* mixer_dev * it = NULL; */ 88 89 ddprintf(("cmedia_pci: mixer_open()\n")); 90 91 *cookie = NULL; 92 for (ix=0; ix<num_cards; ix++) { 93 if (!strcmp(name, cards[ix].mixer.name)) { 94 break; 95 } 96 } 97 if (ix == num_cards) { 98 return ENODEV; 99 } 100 101 atomic_add(&cards[ix].mixer.open_count, 1); 102 cards[ix].mixer.card = &cards[ix]; 103 *cookie = &cards[ix].mixer; 104 105 return B_OK; 106 } 107 108 109 static status_t 110 mixer_close( 111 void * cookie) 112 { 113 mixer_dev * it = (mixer_dev *)cookie; 114 115 atomic_add(&it->open_count, -1); 116 117 return B_OK; 118 } 119 120 121 static status_t 122 mixer_free( 123 void * cookie) 124 { 125 ddprintf(("cmedia_pci: mixer_free()\n")); 126 127 if (((mixer_dev *)cookie)->open_count != 0) { 128 dprintf("cmedia_pci: mixer open_count is bad in mixer_free()!\n"); 129 } 130 return B_OK; /* already done in close */ 131 } 132 133 134 static int 135 get_mixer_value( 136 cmedia_pci_dev * card, 137 cmedia_pci_level * lev) 138 { 139 int ix = map_mixer(lev->selector); 140 uchar val; 141 if (ix < 0) { 142 return B_BAD_VALUE; 143 } 144 val = get_indirect(card, the_mixers[ix].port); 145 lev->flags = 0; 146 if (!the_mixers[ix].mutemask) { 147 /* no change */ 148 } 149 else if (the_mixers[ix].mutemask == 0x01) { 150 if (!(val & 0x01)) { 151 lev->flags |= CMEDIA_PCI_LEVEL_MUTED; 152 } 153 } 154 else if (val & the_mixers[ix].mutemask) { 155 lev->flags |= CMEDIA_PCI_LEVEL_MUTED; 156 } 157 val &= the_mixers[ix].mask; 158 val >>= the_mixers[ix].leftshift; 159 lev->value = ((float)val)*the_mixers[ix].div+the_mixers[ix].sub; 160 161 return B_OK; 162 } 163 164 165 static int 166 gather_info( 167 mixer_dev * mixer, 168 cmedia_pci_level * data, 169 int count) 170 { 171 int ix; 172 cpu_status cp; 173 174 cp = disable_interrupts(); 175 acquire_spinlock(&mixer->card->hardware); 176 177 for (ix=0; ix<count; ix++) { 178 if (get_mixer_value(mixer->card, &data[ix]) < B_OK) 179 break; 180 } 181 182 release_spinlock(&mixer->card->hardware); 183 restore_interrupts(cp); 184 185 return ix; 186 } 187 188 189 static status_t 190 set_mixer_value( 191 cmedia_pci_dev * card, 192 cmedia_pci_level * lev) 193 { 194 int selector = map_mixer(lev->selector); 195 int value; 196 int mask; 197 if (selector < 0) { 198 return EINVAL; 199 } 200 value = (lev->value-the_mixers[selector].sub)/the_mixers[selector].div; 201 if (value < the_mixers[selector].minval) { 202 value = the_mixers[selector].minval; 203 } 204 if (value > the_mixers[selector].maxval) { 205 value = the_mixers[selector].maxval; 206 } 207 value <<= the_mixers[selector].leftshift; 208 if (the_mixers[selector].mutemask) { 209 if (the_mixers[selector].mutemask == 0x01) { 210 if (!(lev->flags & CMEDIA_PCI_LEVEL_MUTED)) { 211 value |= the_mixers[selector].mutemask; 212 } 213 } else { 214 if (lev->flags & CMEDIA_PCI_LEVEL_MUTED) { 215 value |= the_mixers[selector].mutemask; 216 } 217 } 218 } 219 mask = the_mixers[selector].mutemask | the_mixers[selector].mask; 220 set_indirect(card, the_mixers[selector].port, value, mask); 221 return B_OK; 222 } 223 224 225 static int 226 disperse_info( 227 mixer_dev * mixer, 228 cmedia_pci_level * data, 229 int count) 230 { 231 int ix; 232 cpu_status cp; 233 234 cp = disable_interrupts(); 235 acquire_spinlock(&mixer->card->hardware); 236 237 for (ix=0; ix<count; ix++) { 238 if (set_mixer_value(mixer->card, &data[ix]) < B_OK) 239 break; 240 } 241 242 release_spinlock(&mixer->card->hardware); 243 restore_interrupts(cp); 244 245 return ix; 246 } 247 248 249 static status_t 250 mixer_control( 251 void * cookie, 252 uint32 iop, 253 void * data, 254 size_t len) 255 { 256 mixer_dev * it = (mixer_dev *)cookie; 257 status_t err = B_OK; 258 259 if (!data) { 260 return B_BAD_VALUE; 261 } 262 263 ddprintf(("cmedia_pci: mixer_control()\n")); /* slow printing */ 264 265 switch (iop) { 266 case B_MIXER_GET_VALUES: 267 ((cmedia_pci_level_cmd *)data)->count = 268 gather_info(it, ((cmedia_pci_level_cmd *)data)->data, 269 ((cmedia_pci_level_cmd *)data)->count); 270 break; 271 case B_MIXER_SET_VALUES: 272 ((cmedia_pci_level_cmd *)data)->count = 273 disperse_info(it, ((cmedia_pci_level_cmd *)data)->data, 274 ((cmedia_pci_level_cmd *)data)->count); 275 break; 276 default: 277 err = B_BAD_VALUE; 278 break; 279 } 280 return err; 281 } 282 283 284 static status_t 285 mixer_read( 286 void * cookie, 287 off_t pos, 288 void * data, 289 size_t * nread) 290 { 291 return EPERM; 292 } 293 294 295 static status_t 296 mixer_write( 297 void * cookie, 298 off_t pos, 299 const void * data, 300 size_t * nwritten) 301 { 302 return EPERM; 303 } 304 305