587 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Driver for Digigram miXart soundcards4 *5 * DSP firmware management6 *7 * Copyright (c) 2003 by Digigram <alsa@digigram.com>8 */9 10#include <linux/interrupt.h>11#include <linux/pci.h>12#include <linux/firmware.h>13#include <linux/vmalloc.h>14#include <linux/slab.h>15#include <linux/module.h>16#include <linux/io.h>17#include <sound/core.h>18#include "mixart.h"19#include "mixart_mixer.h"20#include "mixart_core.h"21#include "mixart_hwdep.h"22 23 24/**25 * mixart_wait_nice_for_register_value - wait for a value on a peudo register,26 * exit with a timeout27 *28 * @mgr: pointer to miXart manager structure29 * @offset: unsigned pseudo_register base + offset of value30 * @is_egal: wait for the equal value31 * @value: value32 * @timeout: timeout in centisenconds33 */34static int mixart_wait_nice_for_register_value(struct mixart_mgr *mgr,35 u32 offset, int is_egal,36 u32 value, unsigned long timeout)37{38 unsigned long end_time = jiffies + (timeout * HZ / 100);39 u32 read;40 41 do { /* we may take too long time in this loop.42 * so give controls back to kernel if needed.43 */44 cond_resched();45 46 read = readl_be( MIXART_MEM( mgr, offset ));47 if(is_egal) {48 if(read == value) return 0;49 }50 else { /* wait for different value */51 if(read != value) return 0;52 }53 } while ( time_after_eq(end_time, jiffies) );54 55 return -EBUSY;56}57 58 59/*60 structures needed to upload elf code packets 61 */62struct snd_mixart_elf32_ehdr {63 u8 e_ident[16];64 __be16 e_type;65 __be16 e_machine;66 __be32 e_version;67 __be32 e_entry;68 __be32 e_phoff;69 __be32 e_shoff;70 __be32 e_flags;71 __be16 e_ehsize;72 __be16 e_phentsize;73 __be16 e_phnum;74 __be16 e_shentsize;75 __be16 e_shnum;76 __be16 e_shstrndx;77};78 79struct snd_mixart_elf32_phdr {80 __be32 p_type;81 __be32 p_offset;82 __be32 p_vaddr;83 __be32 p_paddr;84 __be32 p_filesz;85 __be32 p_memsz;86 __be32 p_flags;87 __be32 p_align;88};89 90static int mixart_load_elf(struct mixart_mgr *mgr, const struct firmware *dsp )91{92 char elf32_magic_number[4] = {0x7f,'E','L','F'};93 struct snd_mixart_elf32_ehdr *elf_header;94 int i;95 96 elf_header = (struct snd_mixart_elf32_ehdr *)dsp->data;97 for( i=0; i<4; i++ )98 if ( elf32_magic_number[i] != elf_header->e_ident[i] )99 return -EINVAL;100 101 if( elf_header->e_phoff != 0 ) {102 struct snd_mixart_elf32_phdr elf_programheader;103 104 for( i=0; i < be16_to_cpu(elf_header->e_phnum); i++ ) {105 u32 pos = be32_to_cpu(elf_header->e_phoff) + (u32)(i * be16_to_cpu(elf_header->e_phentsize));106 107 memcpy( &elf_programheader, dsp->data + pos, sizeof(elf_programheader) );108 109 if(elf_programheader.p_type != 0) {110 if( elf_programheader.p_filesz != 0 ) {111 memcpy_toio( MIXART_MEM( mgr, be32_to_cpu(elf_programheader.p_vaddr)),112 dsp->data + be32_to_cpu( elf_programheader.p_offset ),113 be32_to_cpu( elf_programheader.p_filesz ));114 }115 }116 }117 }118 return 0;119}120 121/*122 * get basic information and init miXart123 */124 125/* audio IDs for request to the board */126#define MIXART_FIRST_ANA_AUDIO_ID 0127#define MIXART_FIRST_DIG_AUDIO_ID 8128 129static int mixart_enum_connectors(struct mixart_mgr *mgr)130{131 u32 k;132 int err;133 struct mixart_msg request;134 struct mixart_enum_connector_resp *connector;135 struct mixart_audio_info_req *audio_info_req;136 struct mixart_audio_info_resp *audio_info;137 138 connector = kmalloc(sizeof(*connector), GFP_KERNEL);139 audio_info_req = kmalloc(sizeof(*audio_info_req), GFP_KERNEL);140 audio_info = kmalloc(sizeof(*audio_info), GFP_KERNEL);141 if (! connector || ! audio_info_req || ! audio_info) {142 err = -ENOMEM;143 goto __error;144 }145 146 audio_info_req->line_max_level = MIXART_FLOAT_P_22_0_TO_HEX;147 audio_info_req->micro_max_level = MIXART_FLOAT_M_20_0_TO_HEX;148 audio_info_req->cd_max_level = MIXART_FLOAT____0_0_TO_HEX;149 150 request.message_id = MSG_SYSTEM_ENUM_PLAY_CONNECTOR;151 request.uid = (struct mixart_uid){0,0}; /* board num = 0 */152 request.data = NULL;153 request.size = 0;154 155 err = snd_mixart_send_msg(mgr, &request, sizeof(*connector), connector);156 if((err < 0) || (connector->error_code) || (connector->uid_count > MIXART_MAX_PHYS_CONNECTORS)) {157 dev_err(&mgr->pci->dev,158 "error MSG_SYSTEM_ENUM_PLAY_CONNECTOR\n");159 err = -EINVAL;160 goto __error;161 }162 163 for(k=0; k < connector->uid_count; k++) {164 struct mixart_pipe *pipe;165 166 if(k < MIXART_FIRST_DIG_AUDIO_ID) {167 pipe = &mgr->chip[k/2]->pipe_out_ana;168 } else {169 pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_out_dig;170 }171 if(k & 1) {172 pipe->uid_right_connector = connector->uid[k]; /* odd */173 } else {174 pipe->uid_left_connector = connector->uid[k]; /* even */175 }176 177 /* dev_dbg(&mgr->pci->dev, "playback connector[%d].object_id = %x\n", k, connector->uid[k].object_id); */178 179 /* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */180 request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO;181 request.uid = connector->uid[k];182 request.data = audio_info_req;183 request.size = sizeof(*audio_info_req);184 185 err = snd_mixart_send_msg(mgr, &request, sizeof(*audio_info), audio_info);186 if( err < 0 ) {187 dev_err(&mgr->pci->dev,188 "error MSG_CONNECTOR_GET_AUDIO_INFO\n");189 goto __error;190 }191 /*dev_dbg(&mgr->pci->dev, "play analog_info.analog_level_present = %x\n", audio_info->info.analog_info.analog_level_present);*/192 }193 194 request.message_id = MSG_SYSTEM_ENUM_RECORD_CONNECTOR;195 request.uid = (struct mixart_uid){0,0}; /* board num = 0 */196 request.data = NULL;197 request.size = 0;198 199 err = snd_mixart_send_msg(mgr, &request, sizeof(*connector), connector);200 if((err < 0) || (connector->error_code) || (connector->uid_count > MIXART_MAX_PHYS_CONNECTORS)) {201 dev_err(&mgr->pci->dev,202 "error MSG_SYSTEM_ENUM_RECORD_CONNECTOR\n");203 err = -EINVAL;204 goto __error;205 }206 207 for(k=0; k < connector->uid_count; k++) {208 struct mixart_pipe *pipe;209 210 if(k < MIXART_FIRST_DIG_AUDIO_ID) {211 pipe = &mgr->chip[k/2]->pipe_in_ana;212 } else {213 pipe = &mgr->chip[(k-MIXART_FIRST_DIG_AUDIO_ID)/2]->pipe_in_dig;214 }215 if(k & 1) {216 pipe->uid_right_connector = connector->uid[k]; /* odd */217 } else {218 pipe->uid_left_connector = connector->uid[k]; /* even */219 }220 221 /* dev_dbg(&mgr->pci->dev, "capture connector[%d].object_id = %x\n", k, connector->uid[k].object_id); */222 223 /* TODO: really need send_msg MSG_CONNECTOR_GET_AUDIO_INFO for each connector ? perhaps for analog level caps ? */224 request.message_id = MSG_CONNECTOR_GET_AUDIO_INFO;225 request.uid = connector->uid[k];226 request.data = audio_info_req;227 request.size = sizeof(*audio_info_req);228 229 err = snd_mixart_send_msg(mgr, &request, sizeof(*audio_info), audio_info);230 if( err < 0 ) {231 dev_err(&mgr->pci->dev,232 "error MSG_CONNECTOR_GET_AUDIO_INFO\n");233 goto __error;234 }235 /*dev_dbg(&mgr->pci->dev, "rec analog_info.analog_level_present = %x\n", audio_info->info.analog_info.analog_level_present);*/236 }237 err = 0;238 239 __error:240 kfree(connector);241 kfree(audio_info_req);242 kfree(audio_info);243 244 return err;245}246 247static int mixart_enum_physio(struct mixart_mgr *mgr)248{249 u32 k;250 int err;251 struct mixart_msg request;252 struct mixart_uid get_console_mgr;253 struct mixart_return_uid console_mgr;254 struct mixart_uid_enumeration phys_io;255 256 /* get the uid for the console manager */257 get_console_mgr.object_id = 0;258 get_console_mgr.desc = MSG_CONSOLE_MANAGER | 0; /* cardindex = 0 */259 260 request.message_id = MSG_CONSOLE_GET_CLOCK_UID;261 request.uid = get_console_mgr;262 request.data = &get_console_mgr;263 request.size = sizeof(get_console_mgr);264 265 err = snd_mixart_send_msg(mgr, &request, sizeof(console_mgr), &console_mgr);266 267 if( (err < 0) || (console_mgr.error_code != 0) ) {268 dev_dbg(&mgr->pci->dev,269 "error MSG_CONSOLE_GET_CLOCK_UID : err=%x\n",270 console_mgr.error_code);271 return -EINVAL;272 }273 274 /* used later for clock issues ! */275 mgr->uid_console_manager = console_mgr.uid;276 277 request.message_id = MSG_SYSTEM_ENUM_PHYSICAL_IO;278 request.uid = (struct mixart_uid){0,0};279 request.data = &console_mgr.uid;280 request.size = sizeof(console_mgr.uid);281 282 err = snd_mixart_send_msg(mgr, &request, sizeof(phys_io), &phys_io);283 if( (err < 0) || ( phys_io.error_code != 0 ) ) {284 dev_err(&mgr->pci->dev,285 "error MSG_SYSTEM_ENUM_PHYSICAL_IO err(%x) error_code(%x)\n",286 err, phys_io.error_code);287 return -EINVAL;288 }289 290 /* min 2 phys io per card (analog in + analog out) */291 if (phys_io.nb_uid < MIXART_MAX_CARDS * 2)292 return -EINVAL;293 294 for(k=0; k<mgr->num_cards; k++) {295 mgr->chip[k]->uid_in_analog_physio = phys_io.uid[k];296 mgr->chip[k]->uid_out_analog_physio = phys_io.uid[phys_io.nb_uid/2 + k]; 297 }298 299 return 0;300}301 302 303static int mixart_first_init(struct mixart_mgr *mgr)304{305 u32 k;306 int err;307 struct mixart_msg request;308 309 err = mixart_enum_connectors(mgr);310 if (err < 0)311 return err;312 313 err = mixart_enum_physio(mgr);314 if (err < 0)315 return err;316 317 /* send a synchro command to card (necessary to do this before first MSG_STREAM_START_STREAM_GRP_PACKET) */318 /* though why not here */319 request.message_id = MSG_SYSTEM_SEND_SYNCHRO_CMD;320 request.uid = (struct mixart_uid){0,0};321 request.data = NULL;322 request.size = 0;323 /* this command has no data. response is a 32 bit status */324 err = snd_mixart_send_msg(mgr, &request, sizeof(k), &k);325 if( (err < 0) || (k != 0) ) {326 dev_err(&mgr->pci->dev, "error MSG_SYSTEM_SEND_SYNCHRO_CMD\n");327 return err == 0 ? -EINVAL : err;328 }329 330 return 0;331}332 333 334/* firmware base addresses (when hard coded) */335#define MIXART_MOTHERBOARD_XLX_BASE_ADDRESS 0x00600000336 337static int mixart_dsp_load(struct mixart_mgr* mgr, int index, const struct firmware *dsp)338{339 int err, card_index;340 u32 status_xilinx, status_elf, status_daught;341 u32 val;342 343 /* read motherboard xilinx status */344 status_xilinx = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));345 /* read elf status */346 status_elf = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));347 /* read daughterboard xilinx status */348 status_daught = readl_be( MIXART_MEM( mgr,MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));349 350 /* motherboard xilinx status 5 will say that the board is performing a reset */351 if (status_xilinx == 5) {352 dev_err(&mgr->pci->dev, "miXart is resetting !\n");353 return -EAGAIN; /* try again later */354 }355 356 switch (index) {357 case MIXART_MOTHERBOARD_XLX_INDEX:358 359 /* xilinx already loaded ? */ 360 if (status_xilinx == 4) {361 dev_dbg(&mgr->pci->dev, "xilinx is already loaded !\n");362 return 0;363 }364 /* the status should be 0 == "idle" */365 if (status_xilinx != 0) {366 dev_err(&mgr->pci->dev,367 "xilinx load error ! status = %d\n",368 status_xilinx);369 return -EIO; /* modprob -r may help ? */370 }371 372 /* check xilinx validity */373 if (((u32*)(dsp->data))[0] == 0xffffffff)374 return -EINVAL;375 if (dsp->size % 4)376 return -EINVAL;377 378 /* set xilinx status to copying */379 writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));380 381 /* setup xilinx base address */382 writel_be( MIXART_MOTHERBOARD_XLX_BASE_ADDRESS, MIXART_MEM( mgr,MIXART_PSEUDOREG_MXLX_BASE_ADDR_OFFSET ));383 /* setup code size for xilinx file */384 writel_be( dsp->size, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_SIZE_OFFSET ));385 386 /* copy xilinx code */387 memcpy_toio( MIXART_MEM( mgr, MIXART_MOTHERBOARD_XLX_BASE_ADDRESS), dsp->data, dsp->size);388 389 /* set xilinx status to copy finished */390 writel_be( 2, MIXART_MEM( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET ));391 392 /* return, because no further processing needed */393 return 0;394 395 case MIXART_MOTHERBOARD_ELF_INDEX:396 397 if (status_elf == 4) {398 dev_dbg(&mgr->pci->dev, "elf file already loaded !\n");399 return 0;400 }401 402 /* the status should be 0 == "idle" */403 if (status_elf != 0) {404 dev_err(&mgr->pci->dev,405 "elf load error ! status = %d\n",406 status_elf);407 return -EIO; /* modprob -r may help ? */408 }409 410 /* wait for xilinx status == 4 */411 err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_MXLX_STATUS_OFFSET, 1, 4, 500); /* 5sec */412 if (err < 0) {413 dev_err(&mgr->pci->dev, "xilinx was not loaded or "414 "could not be started\n");415 return err;416 }417 418 /* init some data on the card */419 writel_be( 0, MIXART_MEM( mgr, MIXART_PSEUDOREG_BOARDNUMBER ) ); /* set miXart boardnumber to 0 */420 writel_be( 0, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) ); /* reset pointer to flow table on miXart */421 422 /* set elf status to copying */423 writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));424 425 /* process the copying of the elf packets */426 err = mixart_load_elf( mgr, dsp );427 if (err < 0) return err;428 429 /* set elf status to copy finished */430 writel_be( 2, MIXART_MEM( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET ));431 432 /* wait for elf status == 4 */433 err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_ELF_STATUS_OFFSET, 1, 4, 300); /* 3sec */434 if (err < 0) {435 dev_err(&mgr->pci->dev, "elf could not be started\n");436 return err;437 }438 439 /* miXart waits at this point on the pointer to the flow table */440 writel_be( (u32)mgr->flowinfo.addr, MIXART_MEM( mgr, MIXART_FLOWTABLE_PTR ) ); /* give pointer of flow table to miXart */441 442 return 0; /* return, another xilinx file has to be loaded before */443 444 case MIXART_AESEBUBOARD_XLX_INDEX:445 default:446 447 /* elf and xilinx should be loaded */448 if (status_elf != 4 || status_xilinx != 4) {449 dev_err(&mgr->pci->dev, "xilinx or elf not "450 "successfully loaded\n");451 return -EIO; /* modprob -r may help ? */452 }453 454 /* wait for daughter detection != 0 */455 err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DBRD_PRESENCE_OFFSET, 0, 0, 30); /* 300msec */456 if (err < 0) {457 dev_err(&mgr->pci->dev, "error starting elf file\n");458 return err;459 }460 461 /* the board type can now be retrieved */462 mgr->board_type = (DAUGHTER_TYPE_MASK & readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DBRD_TYPE_OFFSET)));463 464 if (mgr->board_type == MIXART_DAUGHTER_TYPE_NONE)465 break; /* no daughter board; the file does not have to be loaded, continue after the switch */466 467 /* only if aesebu daughter board presence (elf code must run) */ 468 if (mgr->board_type != MIXART_DAUGHTER_TYPE_AES )469 return -EINVAL;470 471 /* daughter should be idle */472 if (status_daught != 0) {473 dev_err(&mgr->pci->dev,474 "daughter load error ! status = %d\n",475 status_daught);476 return -EIO; /* modprob -r may help ? */477 }478 479 /* check daughterboard xilinx validity */480 if (((u32*)(dsp->data))[0] == 0xffffffff)481 return -EINVAL;482 if (dsp->size % 4)483 return -EINVAL;484 485 /* inform mixart about the size of the file */486 writel_be( dsp->size, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_SIZE_OFFSET ));487 488 /* set daughterboard status to 1 */489 writel_be( 1, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));490 491 /* wait for status == 2 */492 err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 2, 30); /* 300msec */493 if (err < 0) {494 dev_err(&mgr->pci->dev, "daughter board load error\n");495 return err;496 }497 498 /* get the address where to write the file */499 val = readl_be( MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_BASE_ADDR_OFFSET ));500 if (!val)501 return -EINVAL;502 503 /* copy daughterboard xilinx code */504 memcpy_toio( MIXART_MEM( mgr, val), dsp->data, dsp->size);505 506 /* set daughterboard status to 4 */507 writel_be( 4, MIXART_MEM( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET ));508 509 /* continue with init */510 break;511 } /* end of switch file index*/512 513 /* wait for daughter status == 3 */514 err = mixart_wait_nice_for_register_value( mgr, MIXART_PSEUDOREG_DXLX_STATUS_OFFSET, 1, 3, 300); /* 3sec */515 if (err < 0) {516 dev_err(&mgr->pci->dev,517 "daughter board could not be initialised\n");518 return err;519 }520 521 /* init mailbox (communication with embedded) */522 snd_mixart_init_mailbox(mgr);523 524 /* first communication with embedded */525 err = mixart_first_init(mgr);526 if (err < 0) {527 dev_err(&mgr->pci->dev, "miXart could not be set up\n");528 return err;529 }530 531 /* create devices and mixer in accordance with HW options*/532 for (card_index = 0; card_index < mgr->num_cards; card_index++) {533 struct snd_mixart *chip = mgr->chip[card_index];534 535 err = snd_mixart_create_pcm(chip);536 if (err < 0)537 return err;538 539 if (card_index == 0) {540 err = snd_mixart_create_mixer(chip->mgr);541 if (err < 0)542 return err;543 }544 545 err = snd_card_register(chip->card);546 if (err < 0)547 return err;548 }549 550 dev_dbg(&mgr->pci->dev,551 "miXart firmware downloaded and successfully set up\n");552 553 return 0;554}555 556 557int snd_mixart_setup_firmware(struct mixart_mgr *mgr)558{559 static const char * const fw_files[3] = {560 "miXart8.xlx", "miXart8.elf", "miXart8AES.xlx"561 };562 char path[32];563 564 const struct firmware *fw_entry;565 int i, err;566 567 for (i = 0; i < 3; i++) {568 sprintf(path, "mixart/%s", fw_files[i]);569 if (request_firmware(&fw_entry, path, &mgr->pci->dev)) {570 dev_err(&mgr->pci->dev,571 "miXart: can't load firmware %s\n", path);572 return -ENOENT;573 }574 /* fake hwdep dsp record */575 err = mixart_dsp_load(mgr, i, fw_entry);576 release_firmware(fw_entry);577 if (err < 0)578 return err;579 mgr->dsp_loaded |= 1 << i;580 }581 return 0;582}583 584MODULE_FIRMWARE("mixart/miXart8.xlx");585MODULE_FIRMWARE("mixart/miXart8.elf");586MODULE_FIRMWARE("mixart/miXart8AES.xlx");587