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