18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/*********************************************************************** 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci AudioScience HPI driver 58c2ecf20Sopenharmony_ci Functions for reading DSP code using hotplug firmware loader 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com> 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci***********************************************************************/ 118c2ecf20Sopenharmony_ci#define SOURCEFILE_NAME "hpidspcd.c" 128c2ecf20Sopenharmony_ci#include "hpidspcd.h" 138c2ecf20Sopenharmony_ci#include "hpidebug.h" 148c2ecf20Sopenharmony_ci#include "hpi_version.h" 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_cistruct dsp_code_private { 178c2ecf20Sopenharmony_ci /** Firmware descriptor */ 188c2ecf20Sopenharmony_ci const struct firmware *firmware; 198c2ecf20Sopenharmony_ci struct pci_dev *dev; 208c2ecf20Sopenharmony_ci}; 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------*/ 238c2ecf20Sopenharmony_cishort hpi_dsp_code_open(u32 adapter, void *os_data, struct dsp_code *dsp_code, 248c2ecf20Sopenharmony_ci u32 *os_error_code) 258c2ecf20Sopenharmony_ci{ 268c2ecf20Sopenharmony_ci const struct firmware *firmware; 278c2ecf20Sopenharmony_ci struct pci_dev *dev = os_data; 288c2ecf20Sopenharmony_ci struct code_header header; 298c2ecf20Sopenharmony_ci char fw_name[20]; 308c2ecf20Sopenharmony_ci short err_ret = HPI_ERROR_DSP_FILE_NOT_FOUND; 318c2ecf20Sopenharmony_ci int err; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci sprintf(fw_name, "asihpi/dsp%04x.bin", adapter); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci err = request_firmware(&firmware, fw_name, &dev->dev); 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci if (err || !firmware) { 388c2ecf20Sopenharmony_ci dev_err(&dev->dev, "%d, request_firmware failed for %s\n", 398c2ecf20Sopenharmony_ci err, fw_name); 408c2ecf20Sopenharmony_ci goto error1; 418c2ecf20Sopenharmony_ci } 428c2ecf20Sopenharmony_ci if (firmware->size < sizeof(header)) { 438c2ecf20Sopenharmony_ci dev_err(&dev->dev, "Header size too small %s\n", fw_name); 448c2ecf20Sopenharmony_ci goto error2; 458c2ecf20Sopenharmony_ci } 468c2ecf20Sopenharmony_ci memcpy(&header, firmware->data, sizeof(header)); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if ((header.type != 0x45444F43) || /* "CODE" */ 498c2ecf20Sopenharmony_ci (header.adapter != adapter) 508c2ecf20Sopenharmony_ci || (header.size != firmware->size)) { 518c2ecf20Sopenharmony_ci dev_err(&dev->dev, 528c2ecf20Sopenharmony_ci "Invalid firmware header size %d != file %zd\n", 538c2ecf20Sopenharmony_ci header.size, firmware->size); 548c2ecf20Sopenharmony_ci goto error2; 558c2ecf20Sopenharmony_ci } 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci if (HPI_VER_MAJOR(header.version) != HPI_VER_MAJOR(HPI_VER)) { 588c2ecf20Sopenharmony_ci /* Major version change probably means Host-DSP protocol change */ 598c2ecf20Sopenharmony_ci dev_err(&dev->dev, 608c2ecf20Sopenharmony_ci "Incompatible firmware version DSP image %X != Driver %X\n", 618c2ecf20Sopenharmony_ci header.version, HPI_VER); 628c2ecf20Sopenharmony_ci goto error2; 638c2ecf20Sopenharmony_ci } 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (header.version != HPI_VER) { 668c2ecf20Sopenharmony_ci dev_warn(&dev->dev, 678c2ecf20Sopenharmony_ci "Firmware version mismatch: DSP image %X != Driver %X\n", 688c2ecf20Sopenharmony_ci header.version, HPI_VER); 698c2ecf20Sopenharmony_ci } 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci HPI_DEBUG_LOG(DEBUG, "dsp code %s opened\n", fw_name); 728c2ecf20Sopenharmony_ci dsp_code->pvt = kmalloc(sizeof(*dsp_code->pvt), GFP_KERNEL); 738c2ecf20Sopenharmony_ci if (!dsp_code->pvt) { 748c2ecf20Sopenharmony_ci err_ret = HPI_ERROR_MEMORY_ALLOC; 758c2ecf20Sopenharmony_ci goto error2; 768c2ecf20Sopenharmony_ci } 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci dsp_code->pvt->dev = dev; 798c2ecf20Sopenharmony_ci dsp_code->pvt->firmware = firmware; 808c2ecf20Sopenharmony_ci dsp_code->header = header; 818c2ecf20Sopenharmony_ci dsp_code->block_length = header.size / sizeof(u32); 828c2ecf20Sopenharmony_ci dsp_code->word_count = sizeof(header) / sizeof(u32); 838c2ecf20Sopenharmony_ci return 0; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cierror2: 868c2ecf20Sopenharmony_ci release_firmware(firmware); 878c2ecf20Sopenharmony_cierror1: 888c2ecf20Sopenharmony_ci dsp_code->block_length = 0; 898c2ecf20Sopenharmony_ci return err_ret; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------*/ 938c2ecf20Sopenharmony_civoid hpi_dsp_code_close(struct dsp_code *dsp_code) 948c2ecf20Sopenharmony_ci{ 958c2ecf20Sopenharmony_ci HPI_DEBUG_LOG(DEBUG, "dsp code closed\n"); 968c2ecf20Sopenharmony_ci release_firmware(dsp_code->pvt->firmware); 978c2ecf20Sopenharmony_ci kfree(dsp_code->pvt); 988c2ecf20Sopenharmony_ci} 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------*/ 1018c2ecf20Sopenharmony_civoid hpi_dsp_code_rewind(struct dsp_code *dsp_code) 1028c2ecf20Sopenharmony_ci{ 1038c2ecf20Sopenharmony_ci /* Go back to start of data, after header */ 1048c2ecf20Sopenharmony_ci dsp_code->word_count = sizeof(struct code_header) / sizeof(u32); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------*/ 1088c2ecf20Sopenharmony_cishort hpi_dsp_code_read_word(struct dsp_code *dsp_code, u32 *pword) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci if (dsp_code->word_count + 1 > dsp_code->block_length) 1118c2ecf20Sopenharmony_ci return HPI_ERROR_DSP_FILE_FORMAT; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci *pword = ((u32 *)(dsp_code->pvt->firmware->data))[dsp_code-> 1148c2ecf20Sopenharmony_ci word_count]; 1158c2ecf20Sopenharmony_ci dsp_code->word_count++; 1168c2ecf20Sopenharmony_ci return 0; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci/*-------------------------------------------------------------------*/ 1208c2ecf20Sopenharmony_cishort hpi_dsp_code_read_block(size_t words_requested, 1218c2ecf20Sopenharmony_ci struct dsp_code *dsp_code, u32 **ppblock) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci if (dsp_code->word_count + words_requested > dsp_code->block_length) 1248c2ecf20Sopenharmony_ci return HPI_ERROR_DSP_FILE_FORMAT; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci *ppblock = 1278c2ecf20Sopenharmony_ci ((u32 *)(dsp_code->pvt->firmware->data)) + 1288c2ecf20Sopenharmony_ci dsp_code->word_count; 1298c2ecf20Sopenharmony_ci dsp_code->word_count += words_requested; 1308c2ecf20Sopenharmony_ci return 0; 1318c2ecf20Sopenharmony_ci} 132