static int prox_reg_init(void)
{
	int retval;
	unsigned char ctrl_23_offset;
	unsigned char data_1_offset;
	struct synaptics_rmi4_f12_query_5 query_5;
	struct synaptics_rmi4_f12_query_8 query_8;
	struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;

	retval = synaptics_rmi4_reg_read(rmi4_data,
			prox->query_base_addr + 5,
			query_5.data,
			sizeof(query_5.data));
	if (retval < 0)
		return retval;

	ctrl_23_offset = query_5.ctrl0_is_present +
			query_5.ctrl1_is_present +
			query_5.ctrl2_is_present +
			query_5.ctrl3_is_present +
			query_5.ctrl4_is_present +
			query_5.ctrl5_is_present +
			query_5.ctrl6_is_present +
			query_5.ctrl7_is_present +
			query_5.ctrl8_is_present +
			query_5.ctrl9_is_present +
			query_5.ctrl10_is_present +
			query_5.ctrl11_is_present +
			query_5.ctrl12_is_present +
			query_5.ctrl13_is_present +
			query_5.ctrl14_is_present +
			query_5.ctrl15_is_present +
			query_5.ctrl16_is_present +
			query_5.ctrl17_is_present +
			query_5.ctrl18_is_present +
			query_5.ctrl19_is_present +
			query_5.ctrl20_is_present +
			query_5.ctrl21_is_present +
			query_5.ctrl22_is_present;

	prox->hover_finger_en_addr = prox->control_base_addr + ctrl_23_offset;

	retval = synaptics_rmi4_reg_read(rmi4_data,
			prox->query_base_addr + 8,
			query_8.data,
			sizeof(query_8.data));
	if (retval < 0)
		return retval;

	data_1_offset = query_8.data0_is_present;
	prox->hover_finger_data_addr = prox->data_base_addr + data_1_offset;

	return retval;
}
static int prox_set_hover_finger_en(void)
{
	int retval;
	unsigned char object_report_enable;
	struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;

	retval = synaptics_rmi4_reg_read(rmi4_data,
			prox->hover_finger_en_addr,
			&object_report_enable,
			sizeof(object_report_enable));
	if (retval < 0) {
		dev_err(rmi4_data->pdev->dev.parent,
				"%s: Failed to read from object report enable register\n",
				__func__);
		return retval;
	}

	if (prox->hover_finger_en)
		object_report_enable |= HOVERING_FINGER_EN;
	else
		object_report_enable &= ~HOVERING_FINGER_EN;

	retval = synaptics_rmi4_reg_write(rmi4_data,
			prox->hover_finger_en_addr,
			&object_report_enable,
			sizeof(object_report_enable));
	if (retval < 0) {
		dev_err(rmi4_data->pdev->dev.parent,
				"%s: Failed to write to object report enable register\n",
				__func__);
		return retval;
	}

	return 0;
}
static int apen_reg_init(void)
{
	int retval;
	unsigned char data_offset;
	unsigned char size_of_query8;
	struct synaptics_rmi4_f12_query_8 query_8;
	struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data;

	retval = synaptics_rmi4_reg_read(rmi4_data,
			apen->query_base_addr + 7,
			&size_of_query8,
			sizeof(size_of_query8));
	if (retval < 0)
		return retval;

	retval = synaptics_rmi4_reg_read(rmi4_data,
			apen->query_base_addr + 8,
			query_8.data,
			sizeof(query_8.data));
	if (retval < 0)
		return retval;

	if ((size_of_query8 >= 2) && (query_8.data6_is_present)) {
		data_offset = query_8.data0_is_present +
				query_8.data1_is_present +
				query_8.data2_is_present +
				query_8.data3_is_present +
				query_8.data4_is_present +
				query_8.data5_is_present;
		apen->apen_data_addr = apen->data_base_addr + data_offset;
		retval = apen_pressure(&query_8);
		if (retval < 0)
			return retval;
	} else {
		dev_err(rmi4_data->pdev->dev.parent,
				"%s: Active pen support unavailable\n",
				__func__);
		retval = -ENODEV;
	}

	return retval;
}
static void prox_hover_finger_report(void)
{
	int retval;
	int x;
	int y;
	int z;
	struct prox_finger_data *data;
	struct synaptics_rmi4_data *rmi4_data = prox->rmi4_data;

	data = prox->finger_data;

	retval = synaptics_rmi4_reg_read(rmi4_data,
			prox->hover_finger_data_addr,
			data->proximity_data,
			sizeof(data->proximity_data));
	if (retval < 0) {
		dev_err(rmi4_data->pdev->dev.parent,
				"%s: Failed to read hovering finger data\n",
				__func__);
		return;
	}

	if (data->object_type_and_status != F12_HOVERING_FINGER_STATUS) {
		if (prox->hover_finger_present)
			prox_hover_finger_lift();

		return;
	}

	x = (data->x_msb << 8) | (data->x_lsb);
	y = (data->y_msb << 8) | (data->y_lsb);
	z = HOVER_Z_MAX - data->z;

	input_report_key(prox->prox_dev, BTN_TOUCH, 0);
	input_report_key(prox->prox_dev, BTN_TOOL_FINGER, 1);
	input_report_abs(prox->prox_dev, ABS_X, x);
	input_report_abs(prox->prox_dev, ABS_Y, y);
	input_report_abs(prox->prox_dev, ABS_DISTANCE, z);

	input_sync(prox->prox_dev);

	dev_dbg(rmi4_data->pdev->dev.parent,
			"%s: x = %d y = %d z = %d\n",
			__func__, x, y, z);

	prox->hover_finger_present = true;

	return;
}
static int apen_pressure(struct synaptics_rmi4_f12_query_8 *query_8)
{
	int retval;
	unsigned char ii;
	unsigned char data_reg_presence;
	unsigned char size_of_query_9;
	unsigned char *query_9;
	unsigned char *data_desc;
	struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data;

	data_reg_presence = query_8->data[1];

	size_of_query_9 = query_8->size_of_query9;
	query_9 = kmalloc(size_of_query_9, GFP_KERNEL);

	retval = synaptics_rmi4_reg_read(rmi4_data,
			apen->query_base_addr + 9,
			query_9,
			size_of_query_9);
	if (retval < 0)
		goto exit;

	data_desc = query_9;

	for (ii = 0; ii < 6; ii++) {
		if (!(data_reg_presence & (1 << ii)))
			continue; /* The data register is not present */
		data_desc++; /* Jump over the size entry */
		while (*data_desc & (1 << 7))
			data_desc++;
		data_desc++; /* Go to the next descriptor */
	}

	data_desc++; /* Jump over the size entry */
	/* Check for the presence of subpackets 1 and 2 */
	if ((*data_desc & (3 << 1)) == (3 << 1))
		apen->max_pressure = ACTIVE_PEN_MAX_PRESSURE_16BIT;
	else
		apen->max_pressure = ACTIVE_PEN_MAX_PRESSURE_8BIT;

exit:
	kfree(query_9);

	return retval;
}
static int apen_scan_pdt(void)
{
	int retval;
	unsigned char ii;
	unsigned char page;
	unsigned char intr_count = 0;
	unsigned char intr_off;
	unsigned char intr_src;
	unsigned short addr;
	struct synaptics_rmi4_fn_desc fd;
	struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data;

	for (page = 0; page < PAGES_TO_SERVICE; page++) {
		for (addr = PDT_START; addr > PDT_END; addr -= PDT_ENTRY_SIZE) {
			addr |= (page << 8);

			retval = synaptics_rmi4_reg_read(rmi4_data,
					addr,
					(unsigned char *)&fd,
					sizeof(fd));
			if (retval < 0)
				return retval;

			addr &= ~(MASK_8BIT << 8);

			if (fd.fn_number) {
				dev_dbg(rmi4_data->pdev->dev.parent,
						"%s: Found F%02x\n",
						__func__, fd.fn_number);
				switch (fd.fn_number) {
				case SYNAPTICS_RMI4_F12:
					goto f12_found;
					break;
				}
			} else {
				break;
			}

			intr_count += fd.intr_src_count;
		}
	}

	dev_err(rmi4_data->pdev->dev.parent,
			"%s: Failed to find F12\n",
			__func__);
	return -EINVAL;

f12_found:
	apen->query_base_addr = fd.query_base_addr | (page << 8);
	apen->control_base_addr = fd.ctrl_base_addr | (page << 8);
	apen->data_base_addr = fd.data_base_addr | (page << 8);
	apen->command_base_addr = fd.cmd_base_addr | (page << 8);

	retval = apen_reg_init();
	if (retval < 0) {
		dev_err(rmi4_data->pdev->dev.parent,
				"%s: Failed to initialize active pen registers\n",
				__func__);
		return retval;
	}

	apen->intr_mask = 0;
	intr_src = fd.intr_src_count;
	intr_off = intr_count % 8;
	for (ii = intr_off;
			ii < (intr_src + intr_off);
			ii++) {
		apen->intr_mask |= 1 << ii;
	}

	rmi4_data->intr_mask[0] |= apen->intr_mask;

	addr = rmi4_data->f01_ctrl_base_addr + 1;

	retval = synaptics_rmi4_reg_write(rmi4_data,
			addr,
			&(rmi4_data->intr_mask[0]),
			sizeof(rmi4_data->intr_mask[0]));
	if (retval < 0) {
		dev_err(rmi4_data->pdev->dev.parent,
				"%s: Failed to set interrupt enable bit\n",
				__func__);
		return retval;
	}

	return 0;
}
static void apen_report(void)
{
	int retval;
	int x;
	int y;
	int pressure;
	static int invert = -1;
	struct apen_data_8b_pressure *apen_data_8b;
	struct synaptics_rmi4_data *rmi4_data = apen->rmi4_data;

	retval = synaptics_rmi4_reg_read(rmi4_data,
			apen->apen_data_addr,
			apen->apen_data->data,
			sizeof(apen->apen_data->data));
	if (retval < 0) {
		dev_err(rmi4_data->pdev->dev.parent,
				"%s: Failed to read active pen data\n",
				__func__);
		return;
	}

	if (apen->apen_data->status_pen == 0) {
		if (apen->apen_present)
			apen_lift();

		dev_dbg(rmi4_data->pdev->dev.parent,
				"%s: No active pen data\n",
				__func__);

		return;
	}

	x = (apen->apen_data->x_msb << 8) | (apen->apen_data->x_lsb);
	y = (apen->apen_data->y_msb << 8) | (apen->apen_data->y_lsb);

	if ((x == -1) && (y == -1)) {
		if (apen->apen_present)
			apen_lift();

		dev_dbg(rmi4_data->pdev->dev.parent,
				"%s: Active pen in range but no valid x & y\n",
				__func__);

		return;
	}

	if (!apen->apen_present)
		invert = -1;

	if (invert != -1 && invert != apen->apen_data->status_invert)
		apen_lift();

	invert = apen->apen_data->status_invert;

	if (apen->max_pressure == ACTIVE_PEN_MAX_PRESSURE_16BIT) {
		pressure = (apen->apen_data->pressure_msb << 8) |
				apen->apen_data->pressure_lsb;
		apen->battery_state = apen->apen_data->battery_state;
		apen->pen_id = (apen->apen_data->pen_id_24_31 << 24) |
				(apen->apen_data->pen_id_16_23 << 16) |
				(apen->apen_data->pen_id_8_15 << 8) |
				apen->apen_data->pen_id_0_7;
	} else {
		apen_data_8b = (struct apen_data_8b_pressure *)apen->apen_data;
		pressure = apen_data_8b->pressure_msb;
		apen->battery_state = apen_data_8b->battery_state;
		apen->pen_id = (apen_data_8b->pen_id_24_31 << 24) |
				(apen_data_8b->pen_id_16_23 << 16) |
				(apen_data_8b->pen_id_8_15 << 8) |
				apen_data_8b->pen_id_0_7;
	}

	input_report_key(apen->apen_dev, BTN_TOUCH, pressure > 0 ? 1 : 0);
	input_report_key(apen->apen_dev,
			apen->apen_data->status_invert > 0 ?
			BTN_TOOL_RUBBER : BTN_TOOL_PEN, 1);
	input_report_key(apen->apen_dev,
			BTN_STYLUS, apen->apen_data->status_barrel > 0 ?
			1 : 0);
	input_report_abs(apen->apen_dev, ABS_X, x);
	input_report_abs(apen->apen_dev, ABS_Y, y);
	input_report_abs(apen->apen_dev, ABS_PRESSURE, pressure);

	input_sync(apen->apen_dev);

	dev_dbg(rmi4_data->pdev->dev.parent,
			"%s: Active pen: status = %d, invert = %d, barrel = %d, x = %d, y = %d, pressure = %d\n",
			__func__,
			apen->apen_data->status_pen,
			apen->apen_data->status_invert,
			apen->apen_data->status_barrel,
			x, y, pressure);

	apen->apen_present = true;

	return;
}