/* * DSPG DBMDX SPI interface driver * * Copyright (C) 2014 DSP Group * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ /* #define DEBUG */ #include #include #include #include #if IS_ENABLED(CONFIG_OF) #include #include #endif #include #include #include #include #include "dbmdx-interface.h" #include "dbmdx-va-regmap.h" #include "dbmdx-vqe-regmap.h" #include "dbmdx-spi.h" #define DEFAULT_SPI_WRITE_CHUNK_SIZE 8 #define MAX_SPI_WRITE_CHUNK_SIZE 0x40000 #define DEFAULT_SPI_READ_CHUNK_SIZE 8 #define MAX_SPI_READ_CHUNK_SIZE 8192 #define DBMDX_USE_EXPIRABLE_WAKELOCK 1 #define DBMDX_WAKELOCK_TIMEOUT_MS 70*1000 static DECLARE_WAIT_QUEUE_HEAD(dbmdx_wq); int spi_set_speed(struct dbmdx_private *p, int index) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; struct spi_device *spi = spi_p->client; int ret = 0; u32 bits_per_word = 0; u32 spi_rate = 0; u16 spi_mode = SPI_MODE_0; if (index >= DBMDX_VA_NR_OF_SPEEDS) { dev_err(spi_p->dev, "%s: Invalid speed index %x\n", __func__, index); return -EINVAL; } spi_rate = p->pdata->va_speed_cfg[index].spi_rate - (p->pdata->va_speed_cfg[index].spi_rate % 1000); bits_per_word = p->pdata->va_speed_cfg[index].spi_rate % 100; spi_mode = (u16)(((p->pdata->va_speed_cfg[index].spi_rate % 1000) - bits_per_word) / 100); if (bits_per_word != 8 && bits_per_word != 16 && bits_per_word != 32) bits_per_word = 8; if (spi_mode == 0) spi_mode = SPI_MODE_0; else if (spi_mode == 1) spi_mode = SPI_MODE_1; else if (spi_mode == 2) spi_mode = SPI_MODE_2; else if (spi_mode == 3) spi_mode = SPI_MODE_3; else spi_mode = SPI_MODE_0; if (spi->max_speed_hz != spi_rate || spi->mode != spi_mode) { spi->max_speed_hz = spi_rate; spi->mode = spi_mode; spi->bits_per_word = bits_per_word; spi_p->pdata->bits_per_word = spi->bits_per_word; spi_p->pdata->bytes_per_word = spi->bits_per_word / 8; dev_info(spi_p->dev, "%s Update SPI Max Speed to %d Hz, bpw: %d, mode: %d\n", __func__, spi->max_speed_hz, spi->bits_per_word, spi->mode); ret = spi_setup(spi); if (ret < 0) dev_err(spi_p->dev, "%s:failed %x\n", __func__, ret); } return ret; } ssize_t send_spi_cmd_vqe(struct dbmdx_private *p, u32 command, u16 *response) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; u8 send[4]; u8 recv[4]; ssize_t ret = 0; int retries = 10; send[0] = (command >> 24) & 0xff; send[1] = (command >> 16) & 0xff; send[2] = (command >> 8) & 0xff; send[3] = command & 0xff; ret = send_spi_data(p, send, 4); if (ret < 0) { dev_err(spi_p->dev, "%s: cmd:0x%08X - send_spi_data failed ret:%zd\n", __func__, command, ret); return ret; } usleep_range(DBMDX_USLEEP_SPI_VQE_CMD_AFTER_SEND, DBMDX_USLEEP_SPI_VQE_CMD_AFTER_SEND + 1000); if ((command == (DBMDX_VQE_SET_POWER_STATE_CMD | DBMDX_VQE_SET_POWER_STATE_HIBERNATE)) || (command == DBMDX_VQE_SET_SWITCH_TO_BOOT_CMD)) return 0; /* we need additional sleep till system is ready */ if (command == (DBMDX_VQE_SET_SYSTEM_CONFIG_CMD | DBMDX_VQE_SET_SYSTEM_CONFIG_PRIMARY_CFG)) msleep(DBMDX_MSLEEP_SPI_VQE_SYS_CFG_CMD); /* read response */ do { ret = spi_read(spi_p->client, recv, 4); if (ret < 0) { /* Wait before polling again */ usleep_range(10000, 11000); continue; } /* * Check that the first two bytes of the response match * (the ack is in those bytes) */ if ((send[0] == recv[0]) && (send[1] == recv[1])) { if (response) *response = (recv[2] << 8) | recv[3]; ret = 0; break; } dev_warn(spi_p->dev, "%s: incorrect ack (got 0x%.2x%.2x)\n", __func__, recv[0], recv[1]); ret = -EINVAL; /* Wait before polling again */ usleep_range(10000, 11000); } while (--retries); if (!retries) dev_err(spi_p->dev, "%s: cmd:0x%08X - wrong ack, giving up\n", __func__, command); return ret; } ssize_t send_spi_cmd_va(struct dbmdx_private *p, u32 command, u16 *response) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; char tmp[3]; u8 send[7]; u8 recv[6] = {0, 0, 0, 0, 0, 0}; int ret; dev_dbg(spi_p->dev, "%s: Send 0x%02x\n", __func__, command); p->wakeup_toggle(p); if (response) { ret = snprintf(tmp, 3, "%02x", (command >> 16) & 0xff); send[0] = 0; send[1] = tmp[0]; send[2] = tmp[1]; send[3] = 'r'; ret = send_spi_data(p, send, 4); if (ret != 4) goto out; usleep_range(DBMDX_USLEEP_SPI_VA_CMD_AFTER_SEND, DBMDX_USLEEP_SPI_VA_CMD_AFTER_SEND + 100); if (p->va_debug_mode & DBMDX_DEBUG_MODE_FW_LOG) msleep(DBMDX_MSLEEP_DBG_MODE_CMD_RX); ret = 0; /* The sleep command cannot be acked before the device * goes to sleep */ ret = read_spi_data(p, recv, 5); if (ret < 0) { dev_err(spi_p->dev, "%s:spi_read failed =%d\n", __func__, ret); return ret; } recv[5] = 0; ret = kstrtou16((const char *)&recv[1], 16, response); if (ret < 0) { dev_err(spi_p->dev, "%s failed -%d\n", __func__, ret); dev_err(spi_p->dev, "%s: %x:%x:%x:%x:\n", __func__, recv[1], recv[2], recv[3], recv[4]); return ret; } dev_dbg(spi_p->dev, "%s: Received 0x%02x\n", __func__, *response); ret = 0; } else { ret = snprintf(tmp, 3, "%02x", (command >> 16) & 0xff); if (ret < 0) goto out; send[0] = tmp[0]; send[1] = tmp[1]; send[2] = 'w'; ret = snprintf(tmp, 3, "%02x", (command >> 8) & 0xff); if (ret < 0) goto out; send[3] = tmp[0]; send[4] = tmp[1]; ret = snprintf(tmp, 3, "%02x", command & 0xff); if (ret < 0) goto out; send[5] = tmp[0]; send[6] = tmp[1]; ret = send_spi_data(p, send, 7); if (ret != 7) goto out; ret = 0; } out: usleep_range(DBMDX_USLEEP_SPI_VA_CMD_AFTER_SEND_2, DBMDX_USLEEP_SPI_VA_CMD_AFTER_SEND_2 + 100); if (p->va_debug_mode & DBMDX_DEBUG_MODE_FW_LOG) msleep(DBMDX_MSLEEP_DBG_MODE_CMD_TX); return ret; } ssize_t send_spi_cmd_va_padded(struct dbmdx_private *p, u32 command, u16 *response) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; char tmp[3]; u8 send[DBMDX_VA_SPI_CMD_PADDED_SIZE] = {0}; u8 recv[DBMDX_VA_SPI_CMD_PADDED_SIZE] = {0}; int ret; u32 padded_cmd_w_size = spi_p->pdata->dma_min_buffer_size; u32 padded_cmd_r_size = 5; dev_dbg(spi_p->dev, "%s: Send 0x%02x\n", __func__, command); p->wakeup_toggle(p); if (response) { ret = snprintf(tmp, 3, "%02x", (command >> 16) & 0xff); send[padded_cmd_w_size - 3] = tmp[0]; send[padded_cmd_w_size - 2] = tmp[1]; send[padded_cmd_w_size - 1] = 'r'; ret = send_spi_data(p, send, padded_cmd_w_size); if (ret != padded_cmd_w_size) goto out; usleep_range(DBMDX_USLEEP_SPI_VA_CMD_AFTER_SEND, DBMDX_USLEEP_SPI_VA_CMD_AFTER_SEND + 100); if (p->va_debug_mode & DBMDX_DEBUG_MODE_FW_LOG) msleep(DBMDX_MSLEEP_DBG_MODE_CMD_RX); ret = 0; /* the sleep command cannot be acked before the device * goes to sleep */ ret = read_spi_data(p, recv, padded_cmd_r_size); if (ret < 0) { dev_err(spi_p->dev, "%s:spi_read failed =%d\n", __func__, ret); return ret; } recv[5] = 0; ret = kstrtou16((const char *)&recv[1], 16, response); if (ret < 0) { dev_err(spi_p->dev, "%s failed -%d\n", __func__, ret); dev_err(spi_p->dev, "%s: %x:%x:%x:%x:\n", __func__, recv[1], recv[2], recv[3], recv[4]); return ret; } dev_dbg(spi_p->dev, "%s: Received 0x%02x\n", __func__, *response); ret = 0; } else { ret = snprintf(tmp, 3, "%02x", (command >> 16) & 0xff); if (ret < 0) goto out; send[padded_cmd_w_size - 7] = tmp[0]; send[padded_cmd_w_size - 6] = tmp[1]; send[padded_cmd_w_size - 5] = 'w'; ret = snprintf(tmp, 3, "%02x", (command >> 8) & 0xff); if (ret < 0) goto out; send[padded_cmd_w_size - 4] = tmp[0]; send[padded_cmd_w_size - 3] = tmp[1]; ret = snprintf(tmp, 3, "%02x", command & 0xff); if (ret < 0) goto out; send[padded_cmd_w_size - 2] = tmp[0]; send[padded_cmd_w_size - 1] = tmp[1]; ret = send_spi_data(p, send, padded_cmd_w_size); if (ret != padded_cmd_w_size) goto out; ret = 0; } out: usleep_range(DBMDX_USLEEP_SPI_VA_CMD_AFTER_SEND_2, DBMDX_USLEEP_SPI_VA_CMD_AFTER_SEND_2 + 100); if (p->va_debug_mode & DBMDX_DEBUG_MODE_FW_LOG) msleep(DBMDX_MSLEEP_DBG_MODE_CMD_TX); return ret; } ssize_t read_spi_data(struct dbmdx_private *p, void *buf, size_t len) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; size_t count = spi_p->pdata->read_chunk_size; u32 bytes_per_word = spi_p->pdata->bytes_per_word; u8 *recv = spi_p->pdata->recv; ssize_t i; size_t pad_size = 0; int ret; u8 *d = (u8 *)buf; /* if stuck for more than 10s, something is wrong */ unsigned long timeout = jiffies + msecs_to_jiffies(10000); for (i = 0; i < len; i += count) { if ((i + count) > len) { count = len - i; if (count % (size_t)bytes_per_word != 0) pad_size = (size_t)bytes_per_word - (size_t)(count % (size_t)bytes_per_word); count = count + pad_size; } ret = spi_read(spi_p->client, recv, count); if (ret < 0) { dev_err(spi_p->dev, "%s: spi_read failed\n", __func__); i = -EIO; goto out; } memcpy(d + i, recv, count - pad_size); if (!time_before(jiffies, timeout)) { dev_err(spi_p->dev, "%s: read data timed out after %zd bytes\n", __func__, i); i = -ETIMEDOUT; goto out; } } return len; out: return i; } ssize_t write_spi_data(struct dbmdx_private *p, const u8 *buf, size_t len) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; struct spi_device *spi = spi_p->client; int rc; rc = spi_write(spi, buf, len); if (rc != 0) { dev_err(spi_p->dev, "%s(): error %d writing SR\n", __func__, rc); return rc; } else return len; } ssize_t send_spi_data(struct dbmdx_private *p, const void *buf, size_t len) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; u32 bytes_per_word = spi_p->pdata->bytes_per_word; int ret = 0; const u8 *cmds = (const u8 *)buf; size_t to_copy = len; size_t max_size = (size_t)(spi_p->pdata->write_chunk_size); size_t pad_size = 0; size_t cur_send_size = 0; u8 *send = spi_p->pdata->send; while (to_copy > 0) { if (to_copy < max_size) { memset(send, 0, max_size); memcpy(send, cmds, to_copy); if (to_copy % (size_t)bytes_per_word != 0) pad_size = (size_t)bytes_per_word - (size_t)(to_copy % (size_t)bytes_per_word); cur_send_size = to_copy + pad_size; } else { memcpy(send, cmds, max_size); cur_send_size = max_size; } ret = write_spi_data(p, send, cur_send_size); if (ret < 0) { dev_err(spi_p->dev, "%s: Failed ret=%d\n", __func__, ret); break; } to_copy -= (ret - pad_size); cmds += (ret - pad_size); } return len - to_copy; } int send_spi_cmd_boot(struct dbmdx_private *p, u32 command) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; u8 send[4]; int ret = 0; dev_dbg(spi_p->dev, "%s: command = %x\n", __func__, command); send[0] = 0; send[1] = 0; send[2] = (command >> 16) & 0xff; send[3] = (command >> 8) & 0xff; ret = send_spi_data(p, send, 4); if (ret < 0) { dev_err(spi_p->dev, "%s: ret = %d\n", __func__, ret); return ret; } /* A host command received will blocked until the current audio frame * processing is finished, which can take up to 10 ms */ usleep_range(DBMDX_USLEEP_SPI_VA_CMD_AFTER_BOOT, DBMDX_USLEEP_SPI_VA_CMD_AFTER_BOOT + 1000); return ret; } int spi_verify_boot_checksum(struct dbmdx_private *p, const void *checksum, size_t chksum_len) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; int ret; u8 rx_checksum[11] = {0}; if (!checksum) return 0; if (chksum_len > 8) { dev_err(spi_p->dev, "%s: illegal checksum length\n", __func__); return -EINVAL; } ret = send_spi_cmd_boot(p, DBMDX_READ_CHECKSUM); if (ret < 0) { dev_err(spi_p->dev, "%s: could not read checksum\n", __func__); return -EIO; } ret = read_spi_data(p, (void *)rx_checksum, chksum_len + 3); if (ret < 0) { dev_err(spi_p->dev, "%s: could not read checksum data\n", __func__); return -EIO; } ret = p->verify_checksum(p, checksum, &rx_checksum[3], chksum_len); if (ret) { dev_err(spi_p->dev, "%s: checksum mismatch\n", __func__); return -EILSEQ; } return 0; } int spi_verify_chip_id(struct dbmdx_private *p) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; int ret; u8 idr_read_cmd[] = {0x5A, 0x07, 0x68, 0x00, 0x00, 0x03}; u8 idr_read_result[7] = {0}; u8 chip_rev_id_low_a = 0; u8 chip_rev_id_low_b = 0; u8 chip_rev_id_high = 0; u8 recv_chip_rev_id_high = 0; u8 recv_chip_rev_id_low = 0; if (p->cur_firmware_id == DBMDX_FIRMWARE_ID_DBMD2) { idr_read_cmd[2] = 0x68; chip_rev_id_high = 0x0d; chip_rev_id_low_a = 0xb0; chip_rev_id_low_b = 0xb1; } else if (p->cur_firmware_id == DBMDX_FIRMWARE_ID_DBMD4) { idr_read_cmd[2] = 0x74; chip_rev_id_high = 0xdb; chip_rev_id_low_a = 0x40; chip_rev_id_low_b = 0x40; } else if (p->cur_firmware_id == DBMDX_FIRMWARE_ID_DBMD6) { idr_read_cmd[2] = 0x74; chip_rev_id_high = 0xdb; chip_rev_id_low_a = 0x60; chip_rev_id_low_b = 0x60; } else { idr_read_cmd[2] = 0x74; chip_rev_id_high = 0xdb; chip_rev_id_low_a = 0x80; chip_rev_id_low_b = 0x80; } ret = send_spi_data(p, idr_read_cmd, 6); if (ret < 0) { dev_err(spi_p->dev, "%s: idr_read_cmd ret = %d\n", __func__, ret); return ret; } usleep_range(DBMDX_USLEEP_SPI_VA_CMD_AFTER_BOOT, DBMDX_USLEEP_SPI_VA_CMD_AFTER_BOOT + 1000); ret = read_spi_data(p, (void *)idr_read_result, 7); if (ret < 0) { dev_err(spi_p->dev, "%s: could not idr register data\n", __func__); return -EIO; } /* Verify answer */ if ((idr_read_result[1] != idr_read_cmd[0]) || (idr_read_result[2] != idr_read_cmd[1]) || (idr_read_result[5] != 0x00) || (idr_read_result[6] != 0x00)) { dev_err(spi_p->dev, "%s: Wrong IDR resp: %x:%x:%x:%x:%x:%x\n", __func__, idr_read_result[1], idr_read_result[2], idr_read_result[3], idr_read_result[4], idr_read_result[5], idr_read_result[6]); return -EIO; } recv_chip_rev_id_high = idr_read_result[4]; recv_chip_rev_id_low = idr_read_result[3]; if ((recv_chip_rev_id_high != chip_rev_id_high) || ((recv_chip_rev_id_low != chip_rev_id_low_a) && (recv_chip_rev_id_low != chip_rev_id_low_b))) { dev_err(spi_p->dev, "%s: Wrong chip ID: Received 0x%2x%2x Expected: 0x%2x%2x | 0x%2x%2x\n", __func__, recv_chip_rev_id_high, recv_chip_rev_id_low, chip_rev_id_high, chip_rev_id_low_a, chip_rev_id_high, chip_rev_id_low_b); return -EILSEQ; } dev_info(spi_p->dev, "%s: Chip ID was successfully verified: 0x%2x%2x\n", __func__, recv_chip_rev_id_high, recv_chip_rev_id_low); return 0; } static int spi_can_boot(struct dbmdx_private *p) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; dev_dbg(spi_p->dev, "%s\n", __func__); return 0; } static int spi_prepare_boot(struct dbmdx_private *p) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; int ret = 0; dev_dbg(spi_p->dev, "%s\n", __func__); ret = spi_set_speed(p, DBMDX_VA_SPEED_MAX); return ret; } static int spi_finish_boot(struct dbmdx_private *p) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; int ret = 0; ret = spi_set_speed(p, DBMDX_VA_SPEED_NORMAL); if (ret < 0) dev_err(spi_p->dev, "%s:failed %x\n", __func__, ret); return ret; } static int spi_dump_state(struct chip_interface *chip, char *buf) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)chip->pdata; int off = 0; dev_dbg(spi_p->dev, "%s\n", __func__); off += snprintf(buf + off, PAGE_SIZE - off, "\t===SPI Interface Dump====\n"); off += snprintf(buf + off, PAGE_SIZE - off, "\tSPI Write Chunk Size:\t\t%d\n", spi_p->pdata->write_chunk_size); off += snprintf(buf + off, PAGE_SIZE - off, "\tSPI Read Chunk Size:\t\t%d\n", spi_p->pdata->read_chunk_size); off += snprintf(buf + off, PAGE_SIZE - off, "\tSPI DMA Min Buffer Size:\t\t%d\n", spi_p->pdata->dma_min_buffer_size); off += snprintf(buf + off, PAGE_SIZE - off, "\tInterface resumed:\t%s\n", spi_p->interface_enabled ? "ON" : "OFF"); return off; } static int spi_set_va_firmware_ready(struct dbmdx_private *p) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; dev_dbg(spi_p->dev, "%s\n", __func__); return 0; } static int spi_set_vqe_firmware_ready(struct dbmdx_private *p) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; dev_dbg(spi_p->dev, "%s\n", __func__); return 0; } static void spi_transport_enable(struct dbmdx_private *p, bool enable) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; int ret = 0; dev_dbg(spi_p->dev, "%s (%s)\n", __func__, enable ? "ON" : "OFF"); if (enable) { #if IS_ENABLED(CONFIG_PM_WAKELOCKS) #if IS_ENABLED(DBMDX_USE_EXPIRABLE_WAKELOCK) if (spi_p->ps_nosuspend_wl) __pm_wakeup_event(spi_p->ps_nosuspend_wl, DBMDX_WAKELOCK_TIMEOUT_MS); #else if (spi_p->ps_nosuspend_wl) __pm_stay_awake(spi_p->ps_nosuspend_wl); #endif #endif ret = wait_event_interruptible(dbmdx_wq, spi_p->interface_enabled); if (ret) dev_dbg(spi_p->dev, "%s, waiting for interface was interrupted", __func__); else dev_dbg(spi_p->dev, "%s, interface is active\n", __func__); } if (enable) { p->wakeup_set(p); if (p->asleep) msleep(DBMDX_MSLEEP_SPI_WAKEUP); } else { #if IS_ENABLED(CONFIG_PM_WAKELOCKS) if (spi_p->ps_nosuspend_wl) __pm_relax(spi_p->ps_nosuspend_wl); #endif p->wakeup_release(p); } } static void spi_resume(struct dbmdx_private *p) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; dev_dbg(spi_p->dev, "%s\n", __func__); spi_interface_resume(spi_p); } void spi_interface_resume(struct dbmdx_spi_private *spi_p) { dev_dbg(spi_p->dev, "%s\n", __func__); spi_p->interface_enabled = 1; wake_up_interruptible(&dbmdx_wq); } static void spi_suspend(struct dbmdx_private *p) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; dev_dbg(spi_p->dev, "%s\n", __func__); spi_interface_suspend(spi_p); } void spi_interface_suspend(struct dbmdx_spi_private *spi_p) { dev_dbg(spi_p->dev, "%s\n", __func__); spi_p->interface_enabled = 0; } static int spi_prepare_buffering(struct dbmdx_private *p) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; dev_dbg(spi_p->dev, "%s\n", __func__); return 0; } #define DBMDX_AUDIO_DEBUG_EXT 0 #define DBMDX_SAMPLE_CHUNK_VERIFICATION_FLAG 0xdbd0 static int spi_read_audio_data(struct dbmdx_private *p, void *buf, size_t samples, bool to_read_metadata, size_t *available_samples, size_t *data_offset) { size_t bytes_to_read; int ret; struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; dev_dbg(spi_p->dev, "%s\n", __func__); ret = send_spi_cmd_va(p, DBMDX_VA_READ_AUDIO_BUFFER | samples, NULL); if (ret) { dev_err(p->dev, "%s: failed to request %zu audio samples\n", __func__, samples); ret = -1; goto out; } *available_samples = 0; if (to_read_metadata) *data_offset = 8; else *data_offset = 2; bytes_to_read = samples * 8 * p->bytes_per_sample + *data_offset; ret = read_spi_data(p, buf, bytes_to_read); if (ret != bytes_to_read) { dev_err(p->dev, "%s: read audio failed, %zu bytes to read, res(%d)\n", __func__, bytes_to_read, ret); ret = -1; goto out; } /* Word #4 contains current number of available samples */ if (to_read_metadata) { u16 verif_flag; *available_samples = (size_t)(((u16 *)buf)[3]); verif_flag = (u16)(((u16 *)buf)[2]); #if DBMDX_AUDIO_DEBUG_EXT dev_err(spi_p->dev, "%s: %x:%x:%x:%x:%x:%x:%x:%x\n", __func__, ((u8 *)buf)[0], ((u8 *)buf)[1], ((u8 *)buf)[2], ((u8 *)buf)[3], ((u8 *)buf)[4], ((u8 *)buf)[5], ((u8 *)buf)[6], ((u8 *)buf)[7]); #endif if (verif_flag != DBMDX_SAMPLE_CHUNK_VERIFICATION_FLAG) { *available_samples = 0; dev_err(p->dev, "%s: Flag verificaiton failed %x:%x\n", __func__, ((u8 *)buf)[4], ((u8 *)buf)[5]); ret = -1; goto out; } } else *available_samples = samples; ret = samples; /* FW performes SPI reset after each chunk transaction * Thus delay is required */ usleep_range(DBMDX_USLEEP_SPI_AFTER_CHUNK_READ, DBMDX_USLEEP_SPI_AFTER_CHUNK_READ + 100); out: return ret; } static int spi_finish_buffering(struct dbmdx_private *p) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; int ret = 0; dev_dbg(spi_p->dev, "%s\n", __func__); return ret; } static int spi_prepare_amodel_loading(struct dbmdx_private *p) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; dev_dbg(spi_p->dev, "%s\n", __func__); return 0; } static int spi_finish_amodel_loading(struct dbmdx_private *p) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; dev_dbg(spi_p->dev, "%s\n", __func__); /* do the same as for finishing buffering */ return 0; } static u32 spi_get_read_chunk_size(struct dbmdx_private *p) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; dev_dbg(spi_p->dev, "%s SPI read chunk is %u\n", __func__, spi_p->pdata->read_chunk_size); return spi_p->pdata->read_chunk_size; } static u32 spi_get_write_chunk_size(struct dbmdx_private *p) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; dev_dbg(spi_p->dev, "%s SPI write chunk is %u\n", __func__, spi_p->pdata->write_chunk_size); return spi_p->pdata->write_chunk_size; } static int spi_set_read_chunk_size(struct dbmdx_private *p, u32 size) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; if (size > MAX_SPI_READ_CHUNK_SIZE) { dev_err(spi_p->dev, "%s Error setting SPI read chunk. Max chunk size: %u\n", __func__, MAX_SPI_READ_CHUNK_SIZE); return -EINVAL; } else if ((size % 4) != 0) { dev_err(spi_p->dev, "%s Error SPI read chunk should be multiply of 4\n", __func__); return -EINVAL; } else if (size == 0) spi_p->pdata->read_chunk_size = DEFAULT_SPI_READ_CHUNK_SIZE; else spi_p->pdata->read_chunk_size = size; dev_dbg(spi_p->dev, "%s SPI read chunk was set to %u\n", __func__, spi_p->pdata->read_chunk_size); return 0; } static int spi_set_write_chunk_size(struct dbmdx_private *p, u32 size) { struct dbmdx_spi_private *spi_p = (struct dbmdx_spi_private *)p->chip->pdata; if (size > MAX_SPI_WRITE_CHUNK_SIZE) { dev_err(spi_p->dev, "%s Error setting SPI write chunk. Max chunk size: %u\n", __func__, MAX_SPI_WRITE_CHUNK_SIZE); return -EINVAL; } else if ((size % 4) != 0) { dev_err(spi_p->dev, "%s Error SPI write chunk should be multiply of 4\n", __func__); return -EINVAL; } else if (size == 0) spi_p->pdata->write_chunk_size = DEFAULT_SPI_WRITE_CHUNK_SIZE; else spi_p->pdata->write_chunk_size = size; dev_dbg(spi_p->dev, "%s SPI write chunk was set to %u\n", __func__, spi_p->pdata->write_chunk_size); return 0; } int spi_common_probe(struct spi_device *client) { #if IS_ENABLED(CONFIG_OF) struct device_node *np; #endif int ret; struct dbmdx_spi_private *p; struct dbmdx_spi_data *pdata; dev_dbg(&client->dev, "%s(): dbmdx\n", __func__); p = kzalloc(sizeof(*p), GFP_KERNEL); if (p == NULL) return -ENOMEM; p->client = client; p->dev = &client->dev; p->chip.pdata = p; #if IS_ENABLED(CONFIG_OF) np = p->dev->of_node; if (!np) { dev_err(p->dev, "%s: no devicetree entry\n", __func__); ret = -EINVAL; goto out_err_kfree; } pdata = kzalloc(sizeof(struct dbmdx_spi_data), GFP_KERNEL); if (!pdata) { ret = -ENOMEM; goto out_err_kfree; } #else pdata = dev_get_platdata(&client->dev); if (pdata == NULL) { dev_err(p->dev, "%s: dbmdx, no platform data found\n", __func__); return -ENODEV; } #endif #if IS_ENABLED(CONFIG_OF) ret = of_property_read_u32(np, "spi-max-frequency", &(pdata->spi_speed)); if (ret && ret != -EINVAL) pdata->spi_speed = 2000000; #endif dev_dbg(p->dev, "%s: spi speed is %u\n", __func__, pdata->spi_speed); #if IS_ENABLED(CONFIG_OF) ret = of_property_read_u32(np, "read-chunk-size", &pdata->read_chunk_size); if (ret != 0) { /* * read-chunk-size not set, set it to default */ pdata->read_chunk_size = DEFAULT_SPI_READ_CHUNK_SIZE; dev_info(p->dev, "%s: Setting spi read chunk to default val: %u bytes\n", __func__, pdata->read_chunk_size); } #endif if (pdata->read_chunk_size % 4 != 0) pdata->read_chunk_size += (4 - (pdata->read_chunk_size % 4)); if (pdata->read_chunk_size > MAX_SPI_READ_CHUNK_SIZE) pdata->read_chunk_size = MAX_SPI_READ_CHUNK_SIZE; if (pdata->read_chunk_size == 0) pdata->read_chunk_size = DEFAULT_SPI_READ_CHUNK_SIZE; dev_info(p->dev, "%s: Setting spi read chunk to %u bytes\n", __func__, pdata->read_chunk_size); #if IS_ENABLED(CONFIG_OF) ret = of_property_read_u32(np, "write-chunk-size", &pdata->write_chunk_size); if (ret != 0) { /* * write-chunk-size not set, set it to default */ pdata->write_chunk_size = DEFAULT_SPI_WRITE_CHUNK_SIZE; dev_info(p->dev, "%s: Setting spi write chunk to default val: %u bytes\n", __func__, pdata->write_chunk_size); } #endif if (pdata->write_chunk_size % 4 != 0) pdata->write_chunk_size += (4 - (pdata->write_chunk_size % 4)); if (pdata->write_chunk_size > MAX_SPI_WRITE_CHUNK_SIZE) pdata->write_chunk_size = MAX_SPI_WRITE_CHUNK_SIZE; if (pdata->write_chunk_size == 0) pdata->write_chunk_size = DEFAULT_SPI_WRITE_CHUNK_SIZE; dev_info(p->dev, "%s: Setting spi write chunk to %u bytes\n", __func__, pdata->write_chunk_size); #if IS_ENABLED(CONFIG_OF) ret = of_property_read_u32(np, "dma_min_buffer_size", &pdata->dma_min_buffer_size); if (ret != 0) { /* * read-chunk-size not set, set it to default */ pdata->dma_min_buffer_size = 0; dev_info(p->dev, "%s: Setting Min DMA Cmd Size to default: %u bytes\n", __func__, pdata->dma_min_buffer_size); } #endif if (pdata->dma_min_buffer_size > DBMDX_VA_SPI_CMD_PADDED_SIZE) pdata->dma_min_buffer_size = DBMDX_VA_SPI_CMD_PADDED_SIZE; if (pdata->dma_min_buffer_size < 7 && pdata->dma_min_buffer_size > 0) pdata->dma_min_buffer_size = 7; dev_info(p->dev, "%s: Setting Min DMA Cmd Size to default: %u bytes\n", __func__, pdata->dma_min_buffer_size); p->pdata = pdata; pdata->send = kmalloc(MAX_SPI_WRITE_CHUNK_SIZE, GFP_KERNEL | GFP_DMA); if (!pdata->send) goto out_err_mem_free; pdata->recv = kmalloc(MAX_SPI_READ_CHUNK_SIZE, GFP_KERNEL | GFP_DMA); if (!pdata->recv) goto out_err_mem_free1; #if IS_ENABLED(CONFIG_PM_WAKELOCKS) p->ps_nosuspend_wl = wakeup_source_create("dbmdx_nosuspend_wakelock_spi"); if (p->ps_nosuspend_wl) wakeup_source_add(p->ps_nosuspend_wl); else dev_err(p->dev, "%s: Error creating WS: dbmdx_nosuspend_wakelock_spi\n", __func__); #endif /* fill in chip interface functions */ p->chip.can_boot = spi_can_boot; p->chip.prepare_boot = spi_prepare_boot; p->chip.finish_boot = spi_finish_boot; p->chip.dump = spi_dump_state; p->chip.set_va_firmware_ready = spi_set_va_firmware_ready; p->chip.set_vqe_firmware_ready = spi_set_vqe_firmware_ready; p->chip.transport_enable = spi_transport_enable; p->chip.read = read_spi_data; p->chip.write = send_spi_data; p->chip.send_cmd_vqe = send_spi_cmd_vqe; p->chip.send_cmd_va = send_spi_cmd_va; p->chip.send_cmd_boot = send_spi_cmd_boot; p->chip.verify_boot_checksum = spi_verify_boot_checksum; p->chip.prepare_buffering = spi_prepare_buffering; p->chip.read_audio_data = spi_read_audio_data; p->chip.finish_buffering = spi_finish_buffering; p->chip.prepare_amodel_loading = spi_prepare_amodel_loading; p->chip.finish_amodel_loading = spi_finish_amodel_loading; p->chip.get_write_chunk_size = spi_get_write_chunk_size; p->chip.get_read_chunk_size = spi_get_read_chunk_size; p->chip.set_write_chunk_size = spi_set_write_chunk_size; p->chip.set_read_chunk_size = spi_set_read_chunk_size; p->chip.resume = spi_resume; p->chip.suspend = spi_suspend; p->interface_enabled = 1; spi_set_drvdata(client, &p->chip); dev_info(&client->dev, "%s: successfully probed\n", __func__); ret = 0; goto out; out_err_mem_free1: kfree(pdata->send); #if IS_ENABLED(CONFIG_OF) out_err_kfree: #endif out_err_mem_free: kfree(p); out: return ret; } int spi_common_remove(struct spi_device *client) { struct chip_interface *ci = spi_get_drvdata(client); struct dbmdx_spi_private *p = (struct dbmdx_spi_private *)ci->pdata; #if IS_ENABLED(CONFIG_PM_WAKELOCKS) if (p->ps_nosuspend_wl) { wakeup_source_remove(p->ps_nosuspend_wl); wakeup_source_destroy(p->ps_nosuspend_wl); } #endif kfree(p->pdata->send); kfree(p->pdata->recv); kfree(p); spi_set_drvdata(client, NULL); return 0; }