162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/*********************************************************************** 362306a36Sopenharmony_ci 462306a36Sopenharmony_ci AudioScience HPI driver 562306a36Sopenharmony_ci Functions for reading DSP code using hotplug firmware loader 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci Copyright (C) 1997-2014 AudioScience Inc. <support@audioscience.com> 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci***********************************************************************/ 1162306a36Sopenharmony_ci#define SOURCEFILE_NAME "hpidspcd.c" 1262306a36Sopenharmony_ci#include "hpidspcd.h" 1362306a36Sopenharmony_ci#include "hpidebug.h" 1462306a36Sopenharmony_ci#include "hpi_version.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_cistruct dsp_code_private { 1762306a36Sopenharmony_ci /** Firmware descriptor */ 1862306a36Sopenharmony_ci const struct firmware *firmware; 1962306a36Sopenharmony_ci struct pci_dev *dev; 2062306a36Sopenharmony_ci}; 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_ci/*-------------------------------------------------------------------*/ 2362306a36Sopenharmony_cishort hpi_dsp_code_open(u32 adapter, void *os_data, struct dsp_code *dsp_code, 2462306a36Sopenharmony_ci u32 *os_error_code) 2562306a36Sopenharmony_ci{ 2662306a36Sopenharmony_ci const struct firmware *firmware; 2762306a36Sopenharmony_ci struct pci_dev *dev = os_data; 2862306a36Sopenharmony_ci struct code_header header; 2962306a36Sopenharmony_ci char fw_name[20]; 3062306a36Sopenharmony_ci short err_ret = HPI_ERROR_DSP_FILE_NOT_FOUND; 3162306a36Sopenharmony_ci int err; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci sprintf(fw_name, "asihpi/dsp%04x.bin", adapter); 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci err = request_firmware(&firmware, fw_name, &dev->dev); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci if (err || !firmware) { 3862306a36Sopenharmony_ci dev_err(&dev->dev, "%d, request_firmware failed for %s\n", 3962306a36Sopenharmony_ci err, fw_name); 4062306a36Sopenharmony_ci goto error1; 4162306a36Sopenharmony_ci } 4262306a36Sopenharmony_ci if (firmware->size < sizeof(header)) { 4362306a36Sopenharmony_ci dev_err(&dev->dev, "Header size too small %s\n", fw_name); 4462306a36Sopenharmony_ci goto error2; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci memcpy(&header, firmware->data, sizeof(header)); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci if ((header.type != 0x45444F43) || /* "CODE" */ 4962306a36Sopenharmony_ci (header.adapter != adapter) 5062306a36Sopenharmony_ci || (header.size != firmware->size)) { 5162306a36Sopenharmony_ci dev_err(&dev->dev, 5262306a36Sopenharmony_ci "Invalid firmware header size %d != file %zd\n", 5362306a36Sopenharmony_ci header.size, firmware->size); 5462306a36Sopenharmony_ci goto error2; 5562306a36Sopenharmony_ci } 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci if (HPI_VER_MAJOR(header.version) != HPI_VER_MAJOR(HPI_VER)) { 5862306a36Sopenharmony_ci /* Major version change probably means Host-DSP protocol change */ 5962306a36Sopenharmony_ci dev_err(&dev->dev, 6062306a36Sopenharmony_ci "Incompatible firmware version DSP image %X != Driver %X\n", 6162306a36Sopenharmony_ci header.version, HPI_VER); 6262306a36Sopenharmony_ci goto error2; 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (header.version != HPI_VER) { 6662306a36Sopenharmony_ci dev_warn(&dev->dev, 6762306a36Sopenharmony_ci "Firmware version mismatch: DSP image %X != Driver %X\n", 6862306a36Sopenharmony_ci header.version, HPI_VER); 6962306a36Sopenharmony_ci } 7062306a36Sopenharmony_ci 7162306a36Sopenharmony_ci HPI_DEBUG_LOG(DEBUG, "dsp code %s opened\n", fw_name); 7262306a36Sopenharmony_ci dsp_code->pvt = kmalloc(sizeof(*dsp_code->pvt), GFP_KERNEL); 7362306a36Sopenharmony_ci if (!dsp_code->pvt) { 7462306a36Sopenharmony_ci err_ret = HPI_ERROR_MEMORY_ALLOC; 7562306a36Sopenharmony_ci goto error2; 7662306a36Sopenharmony_ci } 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci dsp_code->pvt->dev = dev; 7962306a36Sopenharmony_ci dsp_code->pvt->firmware = firmware; 8062306a36Sopenharmony_ci dsp_code->header = header; 8162306a36Sopenharmony_ci dsp_code->block_length = header.size / sizeof(u32); 8262306a36Sopenharmony_ci dsp_code->word_count = sizeof(header) / sizeof(u32); 8362306a36Sopenharmony_ci return 0; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_cierror2: 8662306a36Sopenharmony_ci release_firmware(firmware); 8762306a36Sopenharmony_cierror1: 8862306a36Sopenharmony_ci dsp_code->block_length = 0; 8962306a36Sopenharmony_ci return err_ret; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci/*-------------------------------------------------------------------*/ 9362306a36Sopenharmony_civoid hpi_dsp_code_close(struct dsp_code *dsp_code) 9462306a36Sopenharmony_ci{ 9562306a36Sopenharmony_ci HPI_DEBUG_LOG(DEBUG, "dsp code closed\n"); 9662306a36Sopenharmony_ci release_firmware(dsp_code->pvt->firmware); 9762306a36Sopenharmony_ci kfree(dsp_code->pvt); 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/*-------------------------------------------------------------------*/ 10162306a36Sopenharmony_civoid hpi_dsp_code_rewind(struct dsp_code *dsp_code) 10262306a36Sopenharmony_ci{ 10362306a36Sopenharmony_ci /* Go back to start of data, after header */ 10462306a36Sopenharmony_ci dsp_code->word_count = sizeof(struct code_header) / sizeof(u32); 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci/*-------------------------------------------------------------------*/ 10862306a36Sopenharmony_cishort hpi_dsp_code_read_word(struct dsp_code *dsp_code, u32 *pword) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci if (dsp_code->word_count + 1 > dsp_code->block_length) 11162306a36Sopenharmony_ci return HPI_ERROR_DSP_FILE_FORMAT; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci *pword = ((u32 *)(dsp_code->pvt->firmware->data))[dsp_code-> 11462306a36Sopenharmony_ci word_count]; 11562306a36Sopenharmony_ci dsp_code->word_count++; 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_ci/*-------------------------------------------------------------------*/ 12062306a36Sopenharmony_cishort hpi_dsp_code_read_block(size_t words_requested, 12162306a36Sopenharmony_ci struct dsp_code *dsp_code, u32 **ppblock) 12262306a36Sopenharmony_ci{ 12362306a36Sopenharmony_ci if (dsp_code->word_count + words_requested > dsp_code->block_length) 12462306a36Sopenharmony_ci return HPI_ERROR_DSP_FILE_FORMAT; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci *ppblock = 12762306a36Sopenharmony_ci ((u32 *)(dsp_code->pvt->firmware->data)) + 12862306a36Sopenharmony_ci dsp_code->word_count; 12962306a36Sopenharmony_ci dsp_code->word_count += words_requested; 13062306a36Sopenharmony_ci return 0; 13162306a36Sopenharmony_ci} 132