Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 0cbeca78 authored by Liangliang Lu's avatar Liangliang Lu
Browse files

usb: misc: Add support for dynamic switching of redriver channel parameters



The re-driver will not automatically change EQ/FG/OC settings when
switching between USB & DP modes, so we must manually update Channel
A/D EQ/FG/OC settings to match Channels B/C settings.

Ideally, the parameters works for USB 3.1 Gen2 10 Gbps should works
for DP 1.4 8.1Gbps, but things seems not works like this. So we need
to support all channels parameter switch between USB and DP.

Change-Id: I7e32db34d7861aa6807cd3feb59242c7d8be2f57
Signed-off-by: default avatarLiangliang Lu <luliang@codeaurora.org>
parent 4c314540
Loading
Loading
Loading
Loading
+30 −6
Original line number Diff line number Diff line
@@ -11,19 +11,43 @@ Required properties:

Optional properties:
- eq: Equalization value of re-driver channel A/B/C/D, 8 bit.
	eq[0] - eq[3]: Channel A-D parameter for USB.
	eq[4] - eq[7]: Channel A-D parameter for DP.
- flat-gain: Flat gain control value of re-driver channel A/B/C/D, 8 bit.
	flat_gain[0] - flat_gain[3]: Channel A-D parameter for USB.
	flat_gain[4] - flat_gain[7]: Channel A-D parameter for DP.
- output-comp: Output compression value of re-driver channel A/B/C/D,
8 bit.
	output_comp[0] - output_comp[3]: Channel A-D parameter for USB.
	output_comp[4] - output_comp[7]: Channel A-D parameter for DP.
- loss-match: Loss profile matching control value of re-driver channel
A/B/C/D, 8 bit.
	loss_match[0] - loss_match[3]: Channel A-D parameter for USB.
	loss_match[4] - loss_match[7]: Channel A-D parameter for DP.

Example:
	redriver@19 {
		compatible = "onnn,redriver";
		reg = <0x19>;
		extcon = <&pm8150b_pdphy>, <&pm8150b_pdphy>;
		eq = /bits/ 8 <0x5 0x4 0x4 0x5>;
		flat-gain = /bits/ 8 <0x3 0x1 0x1 0x3>;
		output-comp = /bits/ 8 <0x2 0x2 0x2 0x2>;
		loss-match = /bits/ 8 <0x0 0x3 0x3 0x0>;
		eq = /bits/ 8 <
				/* Parameters for USB */
				0x4 0x4 0x4 0x4
				/* Parameters for DP */
				0x6 0x4 0x4 0x6>;
		flat-gain = /bits/ 8 <
				/* Parameters for USB */
				0x3 0x1 0x1 0x3
				/* Parameters for DP */
				0x2 0x1 0x1 0x2>;
		output-comp = /bits/ 8 <
				/* Parameters for USB */
				0x3 0x3 0x3 0x3
				/* Parameters for DP */
				0x3 0x3 0x3 0x3>;
		loss-match = /bits/ 8 <
				/* Parameters for USB */
				0x1 0x3 0x3 0x1
				/* Parameters for DP */
				0x3 0x3 0x3 0x3>;
	};
+234 −105
Original line number Diff line number Diff line
@@ -53,6 +53,11 @@
#define LOSS_MATCH_SHIFT		0x00
#define FLAT_GAIN_SHIFT			0x00

#define CHNA_INDEX		0
#define CHNB_INDEX		1
#define CHNC_INDEX		2
#define CHND_INDEX		3

/* for type c cable */
enum plug_orientation {
	ORIENTATION_NONE,
@@ -72,6 +77,17 @@ enum operation_mode {
	OP_MODE_USB_AND_DP, /* One port of USB and DP 2 Lane */
};

/*
 * USB redriver channel mode:
 *  - USB mode
 *  - DP mode
 */
enum channel_mode {
	CHAN_MODE_USB,
	CHAN_MODE_DP,
	CHAN_MODE_NUM,
};

/**
 * struct ssusb_redriver - representation of USB re-driver
 * @dev: struct device pointer
@@ -91,10 +107,19 @@ enum operation_mode {
 * @id_nb: used for id event reception
 * @dp_nb: used for DP event reception
 * @panic_nb: used for panic event reception
 * @eq: equalization register value
 * @chan_mode: used to indicate re-driver's channel mode
 * @eq: equalization register value.
 *      eq[0] - eq[3]: Channel A-D parameter for USB
 *      eq[4] - eq[7]: Channel A-D parameter for DP
 * @output_comp: output compression register value
 *      output_comp[0] - output_comp[3]: Channel A-D parameter for USB
 *      output_comp[4] - output_comp[7]: Channel A-D parameter for DP
 * @loss_match: loss profile matching control register value
 *      loss_match[0] - loss_match[3]: Channel A-D parameter for USB
 *      loss_match[4] - loss_match[7]: Channel A-D parameter for DP
 * @flat_gain: flat gain control register value
 *      flat_gain[0] - flat_gain[3]: Channel A-D parameter for USB
 *      flat_gain[4] - flat_gain[7]: Channel A-D parameter for DP
 * @debug_root: debugfs entry for this context
 */
struct ssusb_redriver {
@@ -120,14 +145,17 @@ struct ssusb_redriver {

	struct notifier_block	panic_nb;

	u8	eq[CHANNEL_NUM];
	u8	output_comp[CHANNEL_NUM];
	u8	loss_match[CHANNEL_NUM];
	u8	flat_gain[CHANNEL_NUM];
	enum	channel_mode chan_mode[CHANNEL_NUM];

	u8	eq[CHAN_MODE_NUM][CHANNEL_NUM];
	u8	output_comp[CHAN_MODE_NUM][CHANNEL_NUM];
	u8	loss_match[CHAN_MODE_NUM][CHANNEL_NUM];
	u8	flat_gain[CHAN_MODE_NUM][CHANNEL_NUM];

	struct dentry	*debug_root;
};

static int ssusb_redriver_channel_update(struct ssusb_redriver *redriver);
static void ssusb_redriver_debugfs_entries(struct ssusb_redriver *redriver);

static int redriver_i2c_reg_get(struct ssusb_redriver *redriver,
@@ -339,24 +367,54 @@ static int ssusb_redriver_dp_notifier(struct notifier_block *nb,
	struct ssusb_redriver *redriver = container_of(nb,
			struct ssusb_redriver, dp_nb);
	enum operation_mode op_mode;
	int ret = 0;

	dev_dbg(redriver->dev,
		"redriver op mode change: %ld event received\n", dp_lane);

	if (dp_lane == 0)
	switch (dp_lane) {
	case 0:
		op_mode = OP_MODE_USB;
	else if (dp_lane == 2)
		redriver->chan_mode[CHNA_INDEX] = CHAN_MODE_USB;
		redriver->chan_mode[CHNB_INDEX] = CHAN_MODE_USB;
		redriver->chan_mode[CHNC_INDEX] = CHAN_MODE_USB;
		redriver->chan_mode[CHND_INDEX] = CHAN_MODE_USB;
		break;
	case 2:
		op_mode = OP_MODE_USB_AND_DP;
	else if (dp_lane == 4)
		if (redriver->typec_orientation == ORIENTATION_CC1) {
			redriver->chan_mode[CHNA_INDEX] = CHAN_MODE_DP;
			redriver->chan_mode[CHNB_INDEX] = CHAN_MODE_DP;
			redriver->chan_mode[CHNC_INDEX] = CHAN_MODE_USB;
			redriver->chan_mode[CHND_INDEX] = CHAN_MODE_USB;
		} else {
			redriver->chan_mode[CHNA_INDEX] = CHAN_MODE_USB;
			redriver->chan_mode[CHNB_INDEX] = CHAN_MODE_USB;
			redriver->chan_mode[CHNC_INDEX] = CHAN_MODE_DP;
			redriver->chan_mode[CHND_INDEX] = CHAN_MODE_DP;
		}
		break;
	case 4:
		op_mode = OP_MODE_DP;
	else
		redriver->chan_mode[CHNA_INDEX] = CHAN_MODE_DP;
		redriver->chan_mode[CHNB_INDEX] = CHAN_MODE_DP;
		redriver->chan_mode[CHNC_INDEX] = CHAN_MODE_DP;
		redriver->chan_mode[CHND_INDEX] = CHAN_MODE_DP;
		break;
	default:
		return 0;
	}

	if (redriver->op_mode == op_mode)
		return 0;

	redriver->op_mode = op_mode;

	ret = ssusb_redriver_channel_update(redriver);
	if (ret)
		dev_dbg(redriver->dev,
			"redriver channel mode change will continue\n");

	queue_work(redriver->redriver_wq, &redriver->config_work);

	return 0;
@@ -490,14 +548,16 @@ static int ssusb_redriver_extcon_register(struct ssusb_redriver *redriver)

static int ssusb_redriver_param_config(struct ssusb_redriver *redriver,
		u8 reg_base, u8 channel, u8 mask, u8 shift, u8 val,
		u8 *stored_val)
		u8 (*stored_val)[CHANNEL_NUM])
{
	int i, ret = -EINVAL;
	u8 reg_addr, reg_val;
	int i, j, ret = -EINVAL;
	u8 reg_addr, reg_val, real_channel, chan_mode;

	if (channel == CHANNEL_NUM) {
		for (i = 0; i < CHANNEL_NUM; i++) {
			reg_addr = reg_base + (i << 1);
	if (channel == CHANNEL_NUM * CHAN_MODE_NUM) {
		for (i = 0; i < CHAN_MODE_NUM; i++)
			for (j = 0; j < CHANNEL_NUM; j++) {
				if (redriver->chan_mode[j] == i) {
					reg_addr = reg_base + (j << 1);

					ret = redriver_i2c_reg_get(redriver,
							reg_addr, &reg_val);
@@ -511,11 +571,16 @@ static int ssusb_redriver_param_config(struct ssusb_redriver *redriver,
							reg_addr, reg_val);
					if (ret < 0)
						return ret;
				}

			stored_val[i] = val;
				stored_val[i][j] = val;
			}
	} else if (channel < CHANNEL_NUM) {
		reg_addr = reg_base + (channel << 1);
	} else if (channel < CHANNEL_NUM * CHAN_MODE_NUM) {
		real_channel = channel % CHANNEL_NUM;
		chan_mode = channel / CHANNEL_NUM;

		if (redriver->chan_mode[real_channel] == chan_mode) {
			reg_addr = reg_base + (real_channel << 1);

			ret = redriver_i2c_reg_get(redriver,
					reg_addr, &reg_val);
@@ -529,8 +594,9 @@ static int ssusb_redriver_param_config(struct ssusb_redriver *redriver,
					reg_addr, reg_val);
			if (ret < 0)
				return ret;
		}

		stored_val[channel] = val;
		stored_val[chan_mode][real_channel] = val;
	} else {
		dev_err(redriver->dev, "error channel value.\n");
		return ret;
@@ -585,73 +651,84 @@ static int ssusb_redriver_loss_match_config(
		return -EINVAL;
}

static int ssusb_redriver_default_config(struct ssusb_redriver *redriver)
static int ssusb_redriver_channel_update(struct ssusb_redriver *redriver)
{
	struct device_node *node = redriver->dev->of_node;
	int ret = 0, i = 0;
	int ret = 0, i = 0, pos = 0;
	u8 chan_mode;

	if (of_find_property(node, "eq", NULL)) {
		ret = of_property_read_u8_array(node, "eq", redriver->eq,
				ARRAY_SIZE(redriver->eq));
		if (!ret) {
	for (i = 0; i < CHANNEL_NUM; i++) {
				ret = ssusb_redriver_eq_config(
						redriver, i,
						redriver->eq[i]);
		chan_mode = redriver->chan_mode[i];
		pos = i + chan_mode * CHANNEL_NUM;

		ret = ssusb_redriver_eq_config(redriver, pos,
				redriver->eq[chan_mode][i]);
		if (ret)
			goto err;

		ret = ssusb_redriver_flat_gain_config(redriver, pos,
				redriver->flat_gain[chan_mode][i]);
		if (ret)
			goto err;

		ret = ssusb_redriver_output_comp_config(redriver, pos,
				redriver->output_comp[chan_mode][i]);
		if (ret)
			goto err;

		ret = ssusb_redriver_loss_match_config(redriver, pos,
				redriver->loss_match[chan_mode][i]);
		if (ret)
			goto err;
	}
		} else

	dev_dbg(redriver->dev, "redriver channel parameters updated.\n");

	return 0;

err:
	dev_err(redriver->dev, "channel parameters update failure.\n");
	return ret;
}

static int ssusb_redriver_default_config(struct ssusb_redriver *redriver)
{
	struct device_node *node = redriver->dev->of_node;
	int ret = 0;

	if (of_find_property(node, "eq", NULL)) {
		ret = of_property_read_u8_array(node, "eq",
				redriver->eq[0], sizeof(redriver->eq));
		if (ret)
			goto err;
	}

	if (of_find_property(node, "flat-gain", NULL)) {
		ret = of_property_read_u8_array(node,
				"flat-gain", redriver->flat_gain,
				ARRAY_SIZE(redriver->flat_gain));
		if (!ret) {
			for (i = 0; i < CHANNEL_NUM; i++) {
				ret = ssusb_redriver_flat_gain_config(
						redriver, i,
						redriver->flat_gain[i]);
				"flat-gain", redriver->flat_gain[0],
				sizeof(redriver->flat_gain));
		if (ret)
			goto err;
	}
		} else
			goto err;
	}

	if (of_find_property(node, "output-comp", NULL)) {
		ret = of_property_read_u8_array(node,
				"output-comp", redriver->output_comp,
				ARRAY_SIZE(redriver->output_comp));
		if (!ret) {
			for (i = 0; i < CHANNEL_NUM; i++) {
				ret = ssusb_redriver_output_comp_config(
						redriver, i,
						redriver->output_comp[i]);
				"output-comp", redriver->output_comp[0],
				sizeof(redriver->output_comp));
		if (ret)
			goto err;
	}
		} else
			goto err;
	}

	if (of_find_property(node, "loss-match", NULL)) {
		ret = of_property_read_u8_array(node,
				"loss-match", redriver->loss_match,
				ARRAY_SIZE(redriver->loss_match));
		if (!ret) {
			for (i = 0; i < CHANNEL_NUM; i++) {
				ret = ssusb_redriver_loss_match_config(
						redriver, i,
						redriver->loss_match[i]);
				"loss-match", redriver->loss_match[0],
				sizeof(redriver->loss_match));
		if (ret)
			goto err;
	}
		} else

	ret = ssusb_redriver_channel_update(redriver);
	if (ret)
		goto err;
	}

	return 0;

@@ -787,9 +864,9 @@ static ssize_t channel_config_write(struct file *file,
{
	struct seq_file *s = file->private_data;
	struct ssusb_redriver *redriver = s->private;
	char buf[20];
	char buf[40];
	char *token_chan, *token_val, *this_buf;
	int ret = 0;
	int store_offset = 0, ret = 0;

	memset(buf, 0, sizeof(buf));

@@ -799,21 +876,37 @@ static ssize_t channel_config_write(struct file *file,
		return -EFAULT;

	if (isdigit(buf[0])) {
		ret = config_func(redriver, CHANNEL_NUM, buf[0] - '0');
		ret = config_func(redriver, CHANNEL_NUM * CHAN_MODE_NUM,
				buf[0] - '0');
		if (ret < 0)
			goto err;
	} else if (isalpha(buf[0])) {
		while ((token_chan = strsep(&this_buf, " ")) != NULL) {
			if (isalpha(*token_chan)
					&& (__toupper(*token_chan) >= 'A')
					&& (__toupper(*token_chan) <= 'D')) {
			switch (*token_chan) {
			case 'A':
			case 'B':
			case 'C':
			case 'D':
				store_offset = *token_chan - 'A';
				token_val = strsep(&this_buf, " ");
				if (!isdigit(*token_val))
					goto err;
			} else
				break;
			case 'a':
			case 'b':
			case 'c':
			case 'd':
				store_offset = *token_chan - 'a'
					+ CHANNEL_NUM;
				token_val = strsep(&this_buf, " ");
				if (!isdigit(*token_val))
					goto err;
				break;
			default:
				goto err;
			};

			ret = config_func(redriver, *token_chan - 'A',
			ret = config_func(redriver, store_offset,
					*token_val - '0');
			if (ret < 0)
				goto err;
@@ -826,7 +919,9 @@ static ssize_t channel_config_write(struct file *file,

err:
	pr_err("Used to config redriver A/B/C/D channels' parameters\n"
		"1. Set all channels to same value\n"
		"A/B/C/D represent for re-driver parameters for USB\n"
		"a/b/c/d represent for re-driver parameters for DP\n"
		"1. Set all channels to same value(both USB and DP)\n"
		"echo n > [eq|output_comp|flat_gain|loss_match]\n"
		"- eq: Equalization, range 0-7\n"
		"- output_comp: Output Compression, range 0-3\n"
@@ -836,8 +931,10 @@ static ssize_t channel_config_write(struct file *file,
		"echo 1 > eq\n"
		"2. Set two channels to different values leave others unchanged\n"
		"echo [A|B|C|D] n [A|B|C|D] n > [eq|output_comp|flat_gain|loss_match]\n"
		"Example2: set channel B flat gain value 2, set channel C flat gain value 3\n"
		"echo B 2 C 3 > flat_gain\n");
		"Example2: USB mode: set channel B flat gain to 2, set channel C flat gain to 3\n"
		"echo B 2 C 3 > flat_gain\n"
		"Example3: DP mode: set channel A equalization to 6, set channel B equalization to 4\n"
		"echo a 6 b 4 > eq\n");

	return -EFAULT;
}
@@ -846,10 +943,18 @@ static int eq_status(struct seq_file *s, void *p)
{
	struct ssusb_redriver *redriver = s->private;

	seq_puts(s, "\t\t\t A\t B\t C\t D\n");
	seq_printf(s, "Equalization:\t\t %d\t %d\t %d\t %d\t\n",
			redriver->eq[0], redriver->eq[1],
			redriver->eq[2], redriver->eq[3]);
	seq_puts(s, "\t\t\t A(USB)\t B(USB)\t C(USB)\t D(USB)\t"
			"A(DP)\t B(DP)\t C(DP)\t D(DP)\n");
	seq_printf(s, "Equalization:\t\t %d\t %d\t %d\t %d\t"
			"%d\t %d\t %d\t %d\n",
			redriver->eq[CHAN_MODE_USB][CHNA_INDEX],
			redriver->eq[CHAN_MODE_USB][CHNB_INDEX],
			redriver->eq[CHAN_MODE_USB][CHNC_INDEX],
			redriver->eq[CHAN_MODE_USB][CHND_INDEX],
			redriver->eq[CHAN_MODE_DP][CHNA_INDEX],
			redriver->eq[CHAN_MODE_DP][CHNB_INDEX],
			redriver->eq[CHAN_MODE_DP][CHNC_INDEX],
			redriver->eq[CHAN_MODE_DP][CHND_INDEX]);
	return 0;
}

@@ -876,10 +981,18 @@ static int flat_gain_status(struct seq_file *s, void *p)
{
	struct ssusb_redriver *redriver = s->private;

	seq_puts(s, "\t\t\t A\t B\t C\t D\n");
	seq_printf(s, "TX/RX Flat Gain:\t %d\t %d\t %d\t %d\t\n",
			redriver->flat_gain[0], redriver->flat_gain[1],
			redriver->flat_gain[2], redriver->flat_gain[3]);
	seq_puts(s, "\t\t\t A(USB)\t B(USB)\t C(USB)\t D(USB)\t"
			"A(DP)\t B(DP)\t C(DP)\t D(DP)\n");
	seq_printf(s, "TX/RX Flat Gain:\t %d\t %d\t %d\t %d\t"
			"%d\t %d\t %d\t %d\n",
			redriver->flat_gain[CHAN_MODE_USB][CHNA_INDEX],
			redriver->flat_gain[CHAN_MODE_USB][CHNB_INDEX],
			redriver->flat_gain[CHAN_MODE_USB][CHNC_INDEX],
			redriver->flat_gain[CHAN_MODE_USB][CHND_INDEX],
			redriver->flat_gain[CHAN_MODE_DP][CHNA_INDEX],
			redriver->flat_gain[CHAN_MODE_DP][CHNB_INDEX],
			redriver->flat_gain[CHAN_MODE_DP][CHNC_INDEX],
			redriver->flat_gain[CHAN_MODE_DP][CHND_INDEX]);
	return 0;
}

@@ -906,10 +1019,18 @@ static int output_comp_status(struct seq_file *s, void *p)
{
	struct ssusb_redriver *redriver = s->private;

	seq_puts(s, "\t\t\t A\t B\t C\t D\n");
	seq_printf(s, "Output Compression:\t %d\t %d\t %d\t %d\t\n",
			redriver->output_comp[0], redriver->output_comp[1],
			redriver->output_comp[2], redriver->output_comp[3]);
	seq_puts(s, "\t\t\t A(USB)\t B(USB)\t C(USB)\t D(USB)\t"
			"A(DP)\t B(DP)\t C(DP)\t D(DP)\n");
	seq_printf(s, "Output Compression:\t %d\t %d\t %d\t %d\t"
			"%d\t %d\t %d\t %d\n",
			redriver->output_comp[CHAN_MODE_USB][CHNA_INDEX],
			redriver->output_comp[CHAN_MODE_USB][CHNB_INDEX],
			redriver->output_comp[CHAN_MODE_USB][CHNC_INDEX],
			redriver->output_comp[CHAN_MODE_USB][CHND_INDEX],
			redriver->output_comp[CHAN_MODE_DP][CHNA_INDEX],
			redriver->output_comp[CHAN_MODE_DP][CHNB_INDEX],
			redriver->output_comp[CHAN_MODE_DP][CHNC_INDEX],
			redriver->output_comp[CHAN_MODE_DP][CHND_INDEX]);
	return 0;
}

@@ -936,10 +1057,18 @@ static int loss_match_status(struct seq_file *s, void *p)
{
	struct ssusb_redriver *redriver = s->private;

	seq_puts(s, "\t\t\t A\t B\t C\t D\n");
	seq_printf(s, "Loss Profile Match:\t %d\t %d\t %d\t %d\t\n",
			redriver->loss_match[0], redriver->loss_match[1],
			redriver->loss_match[2], redriver->loss_match[3]);
	seq_puts(s, "\t\t\t A(USB)\t B(USB)\t C(USB)\t D(USB)\t"
			"A(DP)\t B(DP)\t C(DP)\t D(DP)\n");
	seq_printf(s, "Loss Profile Match:\t %d\t %d\t %d\t %d\t"
			"%d\t %d\t %d\t %d\n",
			redriver->loss_match[CHAN_MODE_USB][CHNA_INDEX],
			redriver->loss_match[CHAN_MODE_USB][CHNB_INDEX],
			redriver->loss_match[CHAN_MODE_USB][CHNC_INDEX],
			redriver->loss_match[CHAN_MODE_USB][CHND_INDEX],
			redriver->loss_match[CHAN_MODE_DP][CHNA_INDEX],
			redriver->loss_match[CHAN_MODE_DP][CHNB_INDEX],
			redriver->loss_match[CHAN_MODE_DP][CHNC_INDEX],
			redriver->loss_match[CHAN_MODE_DP][CHND_INDEX]);
	return 0;
}