Re: [PATCH v9 2/4] Input: add core support for Goodix Berlin Touchscreen IC

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Neil,

On Sat, Oct 21, 2023 at 01:09:24PM +0200, Neil Armstrong wrote:
> +static int goodix_berlin_get_ic_info(struct goodix_berlin_core *cd)
> +{
> +	u8 afe_data[GOODIX_BERLIN_IC_INFO_MAX_LEN];

You probably already saw the kernel test robot message, I think we
should allocate this buffer in the heap (and free it once its no longer
needed).

> +	__le16 length_raw;
> +	u16 length;
> +	int error;
> +
> +	error = regmap_raw_read(cd->regmap, GOODIX_BERLIN_IC_INFO_ADDR,
> +				&length_raw, sizeof(length_raw));
> +	if (error) {
> +		dev_info(cd->dev, "failed get ic info length, %d\n", error);
> +		return error;
> +	}
> +
> +	length = le16_to_cpu(length_raw);
> +	if (length >= GOODIX_BERLIN_IC_INFO_MAX_LEN) {
> +		dev_info(cd->dev, "invalid ic info length %d\n", length);
> +		return -EINVAL;
> +	}
> +
> +	error = regmap_raw_read(cd->regmap, GOODIX_BERLIN_IC_INFO_ADDR,
> +				afe_data, length);
> +	if (error) {
> +		dev_info(cd->dev, "failed get ic info data, %d\n", error);
> +		return error;
> +	}
> +
> +	/* check whether the data is valid (ex. bus default values) */
> +	if (goodix_berlin_is_dummy_data(cd, (const uint8_t *)afe_data, length)) {

This cast is not needed.

> +		dev_err(cd->dev, "fw info data invalid\n");
> +		return -EINVAL;
> +	}
> +
> +	if (!goodix_berlin_checksum_valid((const uint8_t *)afe_data, length)) {

This cast is not needed either.

> +		dev_info(cd->dev, "fw info checksum error\n");
> +		return -EINVAL;
> +	}
> +
> +	error = goodix_berlin_convert_ic_info(cd, afe_data, length);
> +	if (error)
> +		return error;
> +
> +	/* check some key info */
> +	if (!cd->touch_data_addr) {
> +		dev_err(cd->dev, "touch_data_addr is null\n");
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static void goodix_berlin_parse_finger(struct goodix_berlin_core *cd,
> +				       const void *buf, int touch_num)
> +{
> +	const struct goodix_berlin_touch_data *touch_data = buf;
> +	int i;
> +
> +	/* Check for data validity */
> +	for (i = 0; i < touch_num; i++) {
> +		unsigned int id;
> +
> +		id = FIELD_GET(GOODIX_BERLIN_TOUCH_ID_MASK, touch_data[i].id);
> +
> +		if (id >= GOODIX_BERLIN_MAX_TOUCH) {
> +			dev_warn(cd->dev, "invalid finger id %d\n", id);
> +			return;

Is it important to abort entire packet if one if the slots has incorrect
data? Can we simply skip over these contacts?

> +		}
> +	}
> +
> +	/* Report finger touches */
> +	for (i = 0; i < touch_num; i++) {
> +		input_mt_slot(cd->input_dev,
> +			      FIELD_GET(GOODIX_BERLIN_TOUCH_ID_MASK,
> +					touch_data[i].id));
> +		input_mt_report_slot_state(cd->input_dev, MT_TOOL_FINGER, true);
> +
> +		touchscreen_report_pos(cd->input_dev, &cd->props,
> +				       __le16_to_cpu(touch_data[i].x),
> +				       __le16_to_cpu(touch_data[i].y),
> +				       true);
> +		input_report_abs(cd->input_dev, ABS_MT_TOUCH_MAJOR,
> +				 __le16_to_cpu(touch_data[i].w));
> +	}
> +
> +	input_mt_sync_frame(cd->input_dev);
> +	input_sync(cd->input_dev);
> +}
> +
> +static void goodix_berlin_touch_handler(struct goodix_berlin_core *cd,
> +					const void *pre_buf, u32 pre_buf_len)
> +{
> +	static u8 buffer[GOODIX_BERLIN_IRQ_READ_LEN(GOODIX_BERLIN_MAX_TOUCH)];

No, please no static data. The drivers should be ready to handle more
than one device on a system. If the buffer is large-ish put it into
goodix_berlin_core.


> +	u8 point_type, touch_num;
> +	int error;
> +
> +	/* copy pre-data to buffer */
> +	memcpy(buffer, pre_buf, pre_buf_len);
> +
> +	touch_num = FIELD_GET(GOODIX_BERLIN_TOUCH_COUNT_MASK,
> +			      buffer[GOODIX_BERLIN_REQUEST_TYPE_OFFSET]);
> +
> +	if (touch_num > GOODIX_BERLIN_MAX_TOUCH) {
> +		dev_warn(cd->dev, "invalid touch num %d\n", touch_num);
> +		return;
> +	}
> +
> +	if (touch_num) {
> +		/* read more data if more than 2 touch events */
> +		if (unlikely(touch_num > 2)) {
> +			error = regmap_raw_read(cd->regmap,
> +						cd->touch_data_addr + pre_buf_len,
> +						&buffer[pre_buf_len],
> +						(touch_num - 2) * GOODIX_BERLIN_BYTES_PER_POINT);
> +			if (error) {
> +				dev_err_ratelimited(cd->dev, "failed to get touch data, %d\n",
> +						    error);
> +				return;
> +			}
> +		}
> +
> +		point_type = FIELD_GET(GOODIX_BERLIN_POINT_TYPE_MASK,
> +				       buffer[GOODIX_BERLIN_IRQ_EVENT_HEAD_LEN]);
> +
> +		if (point_type == GOODIX_BERLIN_POINT_TYPE_STYLUS ||
> +		    point_type == GOODIX_BERLIN_POINT_TYPE_STYLUS_HOVER) {
> +			dev_warn_once(cd->dev, "Stylus event type not handled\n");
> +			return;
> +		}
> +
> +		if (!goodix_berlin_checksum_valid(&buffer[GOODIX_BERLIN_IRQ_EVENT_HEAD_LEN],
> +						  touch_num * GOODIX_BERLIN_BYTES_PER_POINT + 2)) {
> +			dev_err(cd->dev, "touch data checksum error, data: %*ph\n",
> +				touch_num * GOODIX_BERLIN_BYTES_PER_POINT + 2,
> +				&buffer[GOODIX_BERLIN_IRQ_EVENT_HEAD_LEN]);
> +			return;
> +		}
> +	}
> +
> +	goodix_berlin_parse_finger(cd, &buffer[GOODIX_BERLIN_IRQ_EVENT_HEAD_LEN],
> +				   touch_num);
> +}
> +
> +static int goodix_berlin_request_handle_reset(struct goodix_berlin_core *cd)
> +{
> +	gpiod_set_value(cd->reset_gpio, 1);
> +	usleep_range(2000, 2100);
> +	gpiod_set_value(cd->reset_gpio, 0);
> +
> +	msleep(GOODIX_BERLIN_NORMAL_RESET_DELAY_MS);

The reset line handling is optional, we should skip waits if there is
no GPIO defined for the board. 

> +
> +	return 0;
> +}
> +
> +static irqreturn_t goodix_berlin_threadirq_func(int irq, void *data)
> +{
> +	struct goodix_berlin_core *cd = data;
> +	u8 buf[GOODIX_BERLIN_IRQ_READ_LEN(2)];
> +	u8 event_status;
> +	int error;
> +
> +	/* First, read buffer with space for 2 touch events */
> +	error = regmap_raw_read(cd->regmap, cd->touch_data_addr, buf,
> +				GOODIX_BERLIN_IRQ_READ_LEN(2));
> +	if (error) {
> +		dev_err_ratelimited(cd->dev, "failed get event head data, %d\n", error);
> +		return IRQ_HANDLED;
> +	}
> +
> +	if (buf[GOODIX_BERLIN_STATUS_OFFSET] == 0)
> +		return IRQ_HANDLED;
> +
> +	if (!goodix_berlin_checksum_valid(buf, GOODIX_BERLIN_IRQ_EVENT_HEAD_LEN)) {
> +		dev_warn_ratelimited(cd->dev, "touch head checksum err : %*ph\n",
> +				     GOODIX_BERLIN_IRQ_EVENT_HEAD_LEN, buf);
> +		return IRQ_HANDLED;
> +	}
> +
> +	event_status = buf[GOODIX_BERLIN_STATUS_OFFSET];
> +
> +	if (event_status & GOODIX_BERLIN_TOUCH_EVENT)
> +		goodix_berlin_touch_handler(cd, buf, GOODIX_BERLIN_IRQ_READ_LEN(2));
> +
> +	if (event_status & GOODIX_BERLIN_REQUEST_EVENT) {
> +		switch (buf[GOODIX_BERLIN_REQUEST_TYPE_OFFSET]) {
> +		case GOODIX_BERLIN_REQUEST_CODE_RESET:
> +			goodix_berlin_request_handle_reset(cd);
> +			break;
> +
> +		default:
> +			dev_warn(cd->dev, "unsupported request code 0x%x\n",
> +				 buf[GOODIX_BERLIN_REQUEST_TYPE_OFFSET]);
> +		}
> +	}
> +
> +	/* Clear up status field */
> +	regmap_write(cd->regmap, cd->touch_data_addr, 0);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int goodix_berlin_input_dev_config(struct goodix_berlin_core *cd,
> +					  const struct input_id *id)
> +{
> +	struct input_dev *input_dev;
> +	int error;
> +
> +	input_dev = devm_input_allocate_device(cd->dev);
> +	if (!input_dev)
> +		return -ENOMEM;
> +
> +	cd->input_dev = input_dev;
> +	input_set_drvdata(input_dev, cd);
> +
> +	input_dev->name = "Goodix Berlin Capacitive TouchScreen";
> +	input_dev->phys = "input/ts";
> +
> +	input_dev->id = *id;
> +
> +	input_set_abs_params(cd->input_dev, ABS_MT_POSITION_X, 0, SZ_64K - 1, 0, 0);
> +	input_set_abs_params(cd->input_dev, ABS_MT_POSITION_Y, 0, SZ_64K - 1, 0, 0);
> +	input_set_abs_params(cd->input_dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0);
> +
> +	touchscreen_parse_properties(cd->input_dev, true, &cd->props);
> +
> +	error = input_mt_init_slots(cd->input_dev, GOODIX_BERLIN_MAX_TOUCH,
> +				    INPUT_MT_DIRECT | INPUT_MT_DROP_UNUSED);
> +	if (error)
> +		return error;
> +
> +	return input_register_device(cd->input_dev);

Please in functions with multiple possible failure paths use format

	error = op(...);
	if (error)
		return error;

	return 0;

Thanks.

-- 
Dmitry



[Index of Archives]     [Linux Media Devel]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Linux Wireless Networking]     [Linux Omap]

  Powered by Linux