Jelajahi Sumber

asoc: regmap: add support for 32bit reg_address regmap_write/read

The regmap write/read can only support 16bit reg_address. but
the reg_address of tambora is 32bit_address. need add support for
it.

Change-Id: I2374b3672bc4b652ef5ed00921a0e3beed0c6231
Signed-off-by: Yuhui Zhao <[email protected]>
Yuhui Zhao 1 tahun lalu
induk
melakukan
2476775b9b
2 mengubah file dengan 107 tambahan dan 13 penghapusan
  1. 2 0
      include/soc/soundwire.h
  2. 105 13
      soc/regmap-swr.c

+ 2 - 0
include/soc/soundwire.h

@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
  * Copyright (c) 2015-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved.
  */
 
 #ifndef _LINUX_SOUNDWIRE_H
@@ -269,6 +270,7 @@ struct swr_device {
 	struct device    dev;
 	u64 addr;
 	u8 group_id;
+	bool paging_support;
 	struct irq_domain *slave_irq;
 	bool slave_irq_pending;
 };

+ 105 - 13
soc/regmap-swr.c

@@ -12,9 +12,68 @@
 #include <linux/init.h>
 #include <soc/soundwire.h>
 
-#define ADDR_BYTES 2
-#define VAL_BYTES 1
-#define PAD_BYTES 0
+#define ADDR_BYTES                (2)
+#define ADDR_BYTES_4              (4)
+#define VAL_BYTES                 (1)
+#define PAD_BYTES                 (0)
+#define SCP1_ADDRESS_VAL_MASK     (0x7f800000)
+#define SCP2_ADDRESS_VAL_MASK     (0x007f8000)
+#define BIT_WIDTH_CHECK_MASK      (0xffff0000)
+#define SCP1_ADDRESS_VAL_SHIFT    (23)
+#define SCP2_ADDRESS_VAL_SHIFT    (15)
+#define SCP1_ADDRESS              (0X48)
+#define SCP2_ADDRESS              (0X49)
+#define SDCA_READ_WRITE_BIT       (0x8000)
+u8 g_scp1_val;
+u8 g_scp2_val;
+static DEFINE_MUTEX(swr_rw_lock);
+
+static int regmap_swr_reg_address_get(struct swr_device *swr,
+			u16 *reg_addr, const void *reg, size_t reg_size)
+{
+	u8 scp1_val = 0, scp2_val = 0;
+	u32 temp = 0;
+	int ret = 0;
+
+	if (reg_size == ADDR_BYTES_4) {
+		temp = (*(u32 *)reg) & SCP1_ADDRESS_VAL_MASK;
+		scp1_val = temp >> SCP1_ADDRESS_VAL_SHIFT;
+
+		temp = (*(u32 *)reg) & SCP2_ADDRESS_VAL_MASK;
+		scp2_val = temp >> SCP2_ADDRESS_VAL_SHIFT;
+
+		if (scp1_val || scp2_val) {
+			if (scp1_val != g_scp1_val) {
+				ret = swr_write(swr, swr->dev_num, SCP1_ADDRESS, &scp1_val);
+				if (ret < 0) {
+					dev_err(&swr->dev, "%s: write reg scp1_address failed, err %d\n",
+						__func__, ret);
+					return ret;
+				}
+				g_scp1_val = scp1_val;
+			}
+
+			if (scp2_val != g_scp2_val) {
+				ret = swr_write(swr, swr->dev_num, SCP2_ADDRESS, &scp2_val);
+				if (ret < 0) {
+					dev_err(&swr->dev, "%s: write reg scp2_address failed, err %d\n",
+					__func__, ret);
+					return ret;
+				}
+				g_scp2_val = scp2_val;
+			}
+			*reg_addr = (*(u16 *)reg | SDCA_READ_WRITE_BIT);
+			dev_dbg(&swr->dev, "%s: reg: 0x%x, scp1_val: 0x%x, scp2_val: 0x%x, reg_addr: 0x%x\n",
+				__func__, *(u32 *)reg, scp1_val, scp2_val, *reg_addr);
+		} else {
+			*reg_addr = *(u16 *)reg;
+		}
+	} else {
+		*reg_addr = *(u16 *)reg;
+	}
+
+	return ret;
+}
 
 static int regmap_swr_gather_write(void *context,
 				const void *reg, size_t reg_size,
@@ -36,12 +95,20 @@ static int regmap_swr_gather_write(void *context,
 		dev_err_ratelimited(dev, "%s: swr device is NULL\n", __func__);
 		return -EINVAL;
 	}
-	if (reg_size != ADDR_BYTES) {
+
+	if ((reg_size != ADDR_BYTES) && (reg_size != ADDR_BYTES_4)) {
 		dev_err_ratelimited(dev, "%s: reg size %zd bytes not supported\n",
 			__func__, reg_size);
 		return -EINVAL;
 	}
-	reg_addr = *(u16 *)reg;
+
+	mutex_lock(&swr_rw_lock);
+	ret = regmap_swr_reg_address_get(swr, &reg_addr, reg, reg_size);
+	if (ret < 0) {
+		mutex_unlock(&swr_rw_lock);
+		return ret;
+	}
+
 	/* val_len = VAL_BYTES * val_count */
 	for (i = 0; i < (val_len / VAL_BYTES); i++) {
 		value = (u8 *)val + (VAL_BYTES * i);
@@ -51,7 +118,10 @@ static int regmap_swr_gather_write(void *context,
 				__func__, (reg_addr + i), ret);
 			break;
 		}
+		dev_dbg(dev, "%s: dev_num: 0x%x, gather write reg: 0x%x, value: 0x%x\n",
+				__func__, swr->dev_num, (reg_addr + i), *value);
 	}
+	mutex_unlock(&swr_rw_lock);
 	return ret;
 }
 
@@ -114,21 +184,30 @@ mem_fail:
 static int regmap_swr_write(void *context, const void *data, size_t count)
 {
 	struct device *dev = context;
+	struct swr_device *swr = to_swr_device(dev);
 	struct regmap *map = dev_get_regmap(dev, NULL);
+	int addr_bytes = 0;
 
 	if (map == NULL) {
 		dev_err_ratelimited(dev, "%s: regmap is NULL\n", __func__);
 		return -EINVAL;
 	}
 
-	WARN_ON(count < ADDR_BYTES);
+	if (swr == NULL) {
+		dev_err_ratelimited(dev, "%s: swr is NULL\n", __func__);
+		return -EINVAL;
+	}
+
+	addr_bytes = (swr->paging_support ? ADDR_BYTES_4 : ADDR_BYTES);
 
-	if (count > (ADDR_BYTES + VAL_BYTES + PAD_BYTES))
+	WARN_ON(count < addr_bytes);
+
+	if (count > (addr_bytes + VAL_BYTES + PAD_BYTES))
 		return regmap_swr_raw_multi_reg_write(context, data, count);
 	else
-		return regmap_swr_gather_write(context, data, ADDR_BYTES,
-					       (data + ADDR_BYTES),
-					       (count - ADDR_BYTES));
+		return regmap_swr_gather_write(context, data, addr_bytes,
+					       (data + addr_bytes),
+					       (count - addr_bytes));
 }
 
 static int regmap_swr_read(void *context,
@@ -149,16 +228,29 @@ static int regmap_swr_read(void *context,
 		dev_err_ratelimited(dev, "%s: swr is NULL\n", __func__);
 		return -EINVAL;
 	}
-	if (reg_size != ADDR_BYTES) {
-		dev_err_ratelimited(dev, "%s: register size %zd bytes not supported\n",
+
+	if ((reg_size != ADDR_BYTES) && (reg_size != ADDR_BYTES_4)) {
+		dev_err_ratelimited(dev, "%s: reg size %zd bytes not supported\n",
 			__func__, reg_size);
 		return -EINVAL;
 	}
-	reg_addr = *(u16 *)reg;
+
+	mutex_lock(&swr_rw_lock);
+	ret = regmap_swr_reg_address_get(swr, &reg_addr, reg, reg_size);
+	if (ret < 0) {
+		dev_err_ratelimited(dev,
+			"%s: regmap_swr_reg_address_get failed, reg: 0x%x\n",
+					__func__, *(u32 *)reg);
+		mutex_unlock(&swr_rw_lock);
+		return ret;
+	}
+
 	ret = swr_read(swr, swr->dev_num, reg_addr, val, val_size);
 	if (ret < 0)
 		dev_err_ratelimited(dev, "%s: codec reg 0x%x read failed %d\n",
 			__func__, reg_addr, ret);
+
+	mutex_unlock(&swr_rw_lock);
 	return ret;
 }