18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * MIPI Display Bus Interface (DBI) LCD controller support
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright 2016 Noralf Trønnes
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
98c2ecf20Sopenharmony_ci#include <linux/delay.h>
108c2ecf20Sopenharmony_ci#include <linux/dma-buf.h>
118c2ecf20Sopenharmony_ci#include <linux/gpio/consumer.h>
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/regulator/consumer.h>
148c2ecf20Sopenharmony_ci#include <linux/spi/spi.h>
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <drm/drm_connector.h>
178c2ecf20Sopenharmony_ci#include <drm/drm_damage_helper.h>
188c2ecf20Sopenharmony_ci#include <drm/drm_drv.h>
198c2ecf20Sopenharmony_ci#include <drm/drm_gem_cma_helper.h>
208c2ecf20Sopenharmony_ci#include <drm/drm_format_helper.h>
218c2ecf20Sopenharmony_ci#include <drm/drm_fourcc.h>
228c2ecf20Sopenharmony_ci#include <drm/drm_gem_framebuffer_helper.h>
238c2ecf20Sopenharmony_ci#include <drm/drm_mipi_dbi.h>
248c2ecf20Sopenharmony_ci#include <drm/drm_modes.h>
258c2ecf20Sopenharmony_ci#include <drm/drm_probe_helper.h>
268c2ecf20Sopenharmony_ci#include <drm/drm_rect.h>
278c2ecf20Sopenharmony_ci#include <video/mipi_display.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define MIPI_DBI_MAX_SPI_READ_SPEED 2000000 /* 2MHz */
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ci#define DCS_POWER_MODE_DISPLAY			BIT(2)
328c2ecf20Sopenharmony_ci#define DCS_POWER_MODE_DISPLAY_NORMAL_MODE	BIT(3)
338c2ecf20Sopenharmony_ci#define DCS_POWER_MODE_SLEEP_MODE		BIT(4)
348c2ecf20Sopenharmony_ci#define DCS_POWER_MODE_PARTIAL_MODE		BIT(5)
358c2ecf20Sopenharmony_ci#define DCS_POWER_MODE_IDLE_MODE		BIT(6)
368c2ecf20Sopenharmony_ci#define DCS_POWER_MODE_RESERVED_MASK		(BIT(0) | BIT(1) | BIT(7))
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci/**
398c2ecf20Sopenharmony_ci * DOC: overview
408c2ecf20Sopenharmony_ci *
418c2ecf20Sopenharmony_ci * This library provides helpers for MIPI Display Bus Interface (DBI)
428c2ecf20Sopenharmony_ci * compatible display controllers.
438c2ecf20Sopenharmony_ci *
448c2ecf20Sopenharmony_ci * Many controllers for tiny lcd displays are MIPI compliant and can use this
458c2ecf20Sopenharmony_ci * library. If a controller uses registers 0x2A and 0x2B to set the area to
468c2ecf20Sopenharmony_ci * update and uses register 0x2C to write to frame memory, it is most likely
478c2ecf20Sopenharmony_ci * MIPI compliant.
488c2ecf20Sopenharmony_ci *
498c2ecf20Sopenharmony_ci * Only MIPI Type 1 displays are supported since a full frame memory is needed.
508c2ecf20Sopenharmony_ci *
518c2ecf20Sopenharmony_ci * There are 3 MIPI DBI implementation types:
528c2ecf20Sopenharmony_ci *
538c2ecf20Sopenharmony_ci * A. Motorola 6800 type parallel bus
548c2ecf20Sopenharmony_ci *
558c2ecf20Sopenharmony_ci * B. Intel 8080 type parallel bus
568c2ecf20Sopenharmony_ci *
578c2ecf20Sopenharmony_ci * C. SPI type with 3 options:
588c2ecf20Sopenharmony_ci *
598c2ecf20Sopenharmony_ci *    1. 9-bit with the Data/Command signal as the ninth bit
608c2ecf20Sopenharmony_ci *    2. Same as above except it's sent as 16 bits
618c2ecf20Sopenharmony_ci *    3. 8-bit with the Data/Command signal as a separate D/CX pin
628c2ecf20Sopenharmony_ci *
638c2ecf20Sopenharmony_ci * Currently mipi_dbi only supports Type C options 1 and 3 with
648c2ecf20Sopenharmony_ci * mipi_dbi_spi_init().
658c2ecf20Sopenharmony_ci */
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci#define MIPI_DBI_DEBUG_COMMAND(cmd, data, len) \
688c2ecf20Sopenharmony_ci({ \
698c2ecf20Sopenharmony_ci	if (!len) \
708c2ecf20Sopenharmony_ci		DRM_DEBUG_DRIVER("cmd=%02x\n", cmd); \
718c2ecf20Sopenharmony_ci	else if (len <= 32) \
728c2ecf20Sopenharmony_ci		DRM_DEBUG_DRIVER("cmd=%02x, par=%*ph\n", cmd, (int)len, data);\
738c2ecf20Sopenharmony_ci	else \
748c2ecf20Sopenharmony_ci		DRM_DEBUG_DRIVER("cmd=%02x, len=%zu\n", cmd, len); \
758c2ecf20Sopenharmony_ci})
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_cistatic const u8 mipi_dbi_dcs_read_commands[] = {
788c2ecf20Sopenharmony_ci	MIPI_DCS_GET_DISPLAY_ID,
798c2ecf20Sopenharmony_ci	MIPI_DCS_GET_RED_CHANNEL,
808c2ecf20Sopenharmony_ci	MIPI_DCS_GET_GREEN_CHANNEL,
818c2ecf20Sopenharmony_ci	MIPI_DCS_GET_BLUE_CHANNEL,
828c2ecf20Sopenharmony_ci	MIPI_DCS_GET_DISPLAY_STATUS,
838c2ecf20Sopenharmony_ci	MIPI_DCS_GET_POWER_MODE,
848c2ecf20Sopenharmony_ci	MIPI_DCS_GET_ADDRESS_MODE,
858c2ecf20Sopenharmony_ci	MIPI_DCS_GET_PIXEL_FORMAT,
868c2ecf20Sopenharmony_ci	MIPI_DCS_GET_DISPLAY_MODE,
878c2ecf20Sopenharmony_ci	MIPI_DCS_GET_SIGNAL_MODE,
888c2ecf20Sopenharmony_ci	MIPI_DCS_GET_DIAGNOSTIC_RESULT,
898c2ecf20Sopenharmony_ci	MIPI_DCS_READ_MEMORY_START,
908c2ecf20Sopenharmony_ci	MIPI_DCS_READ_MEMORY_CONTINUE,
918c2ecf20Sopenharmony_ci	MIPI_DCS_GET_SCANLINE,
928c2ecf20Sopenharmony_ci	MIPI_DCS_GET_DISPLAY_BRIGHTNESS,
938c2ecf20Sopenharmony_ci	MIPI_DCS_GET_CONTROL_DISPLAY,
948c2ecf20Sopenharmony_ci	MIPI_DCS_GET_POWER_SAVE,
958c2ecf20Sopenharmony_ci	MIPI_DCS_GET_CABC_MIN_BRIGHTNESS,
968c2ecf20Sopenharmony_ci	MIPI_DCS_READ_DDB_START,
978c2ecf20Sopenharmony_ci	MIPI_DCS_READ_DDB_CONTINUE,
988c2ecf20Sopenharmony_ci	0, /* sentinel */
998c2ecf20Sopenharmony_ci};
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic bool mipi_dbi_command_is_read(struct mipi_dbi *dbi, u8 cmd)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	unsigned int i;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	if (!dbi->read_commands)
1068c2ecf20Sopenharmony_ci		return false;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	for (i = 0; i < 0xff; i++) {
1098c2ecf20Sopenharmony_ci		if (!dbi->read_commands[i])
1108c2ecf20Sopenharmony_ci			return false;
1118c2ecf20Sopenharmony_ci		if (cmd == dbi->read_commands[i])
1128c2ecf20Sopenharmony_ci			return true;
1138c2ecf20Sopenharmony_ci	}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci	return false;
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci/**
1198c2ecf20Sopenharmony_ci * mipi_dbi_command_read - MIPI DCS read command
1208c2ecf20Sopenharmony_ci * @dbi: MIPI DBI structure
1218c2ecf20Sopenharmony_ci * @cmd: Command
1228c2ecf20Sopenharmony_ci * @val: Value read
1238c2ecf20Sopenharmony_ci *
1248c2ecf20Sopenharmony_ci * Send MIPI DCS read command to the controller.
1258c2ecf20Sopenharmony_ci *
1268c2ecf20Sopenharmony_ci * Returns:
1278c2ecf20Sopenharmony_ci * Zero on success, negative error code on failure.
1288c2ecf20Sopenharmony_ci */
1298c2ecf20Sopenharmony_ciint mipi_dbi_command_read(struct mipi_dbi *dbi, u8 cmd, u8 *val)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	if (!dbi->read_commands)
1328c2ecf20Sopenharmony_ci		return -EACCES;
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	if (!mipi_dbi_command_is_read(dbi, cmd))
1358c2ecf20Sopenharmony_ci		return -EINVAL;
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	return mipi_dbi_command_buf(dbi, cmd, val, 1);
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mipi_dbi_command_read);
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci/**
1428c2ecf20Sopenharmony_ci * mipi_dbi_command_buf - MIPI DCS command with parameter(s) in an array
1438c2ecf20Sopenharmony_ci * @dbi: MIPI DBI structure
1448c2ecf20Sopenharmony_ci * @cmd: Command
1458c2ecf20Sopenharmony_ci * @data: Parameter buffer
1468c2ecf20Sopenharmony_ci * @len: Buffer length
1478c2ecf20Sopenharmony_ci *
1488c2ecf20Sopenharmony_ci * Returns:
1498c2ecf20Sopenharmony_ci * Zero on success, negative error code on failure.
1508c2ecf20Sopenharmony_ci */
1518c2ecf20Sopenharmony_ciint mipi_dbi_command_buf(struct mipi_dbi *dbi, u8 cmd, u8 *data, size_t len)
1528c2ecf20Sopenharmony_ci{
1538c2ecf20Sopenharmony_ci	u8 *cmdbuf;
1548c2ecf20Sopenharmony_ci	int ret;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	/* SPI requires dma-safe buffers */
1578c2ecf20Sopenharmony_ci	cmdbuf = kmemdup(&cmd, 1, GFP_KERNEL);
1588c2ecf20Sopenharmony_ci	if (!cmdbuf)
1598c2ecf20Sopenharmony_ci		return -ENOMEM;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	mutex_lock(&dbi->cmdlock);
1628c2ecf20Sopenharmony_ci	ret = dbi->command(dbi, cmdbuf, data, len);
1638c2ecf20Sopenharmony_ci	mutex_unlock(&dbi->cmdlock);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	kfree(cmdbuf);
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	return ret;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mipi_dbi_command_buf);
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci/* This should only be used by mipi_dbi_command() */
1728c2ecf20Sopenharmony_ciint mipi_dbi_command_stackbuf(struct mipi_dbi *dbi, u8 cmd, const u8 *data,
1738c2ecf20Sopenharmony_ci			      size_t len)
1748c2ecf20Sopenharmony_ci{
1758c2ecf20Sopenharmony_ci	u8 *buf;
1768c2ecf20Sopenharmony_ci	int ret;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	buf = kmemdup(data, len, GFP_KERNEL);
1798c2ecf20Sopenharmony_ci	if (!buf)
1808c2ecf20Sopenharmony_ci		return -ENOMEM;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	ret = mipi_dbi_command_buf(dbi, cmd, buf, len);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	kfree(buf);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	return ret;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mipi_dbi_command_stackbuf);
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci/**
1918c2ecf20Sopenharmony_ci * mipi_dbi_buf_copy - Copy a framebuffer, transforming it if necessary
1928c2ecf20Sopenharmony_ci * @dst: The destination buffer
1938c2ecf20Sopenharmony_ci * @fb: The source framebuffer
1948c2ecf20Sopenharmony_ci * @clip: Clipping rectangle of the area to be copied
1958c2ecf20Sopenharmony_ci * @swap: When true, swap MSB/LSB of 16-bit values
1968c2ecf20Sopenharmony_ci *
1978c2ecf20Sopenharmony_ci * Returns:
1988c2ecf20Sopenharmony_ci * Zero on success, negative error code on failure.
1998c2ecf20Sopenharmony_ci */
2008c2ecf20Sopenharmony_ciint mipi_dbi_buf_copy(void *dst, struct drm_framebuffer *fb,
2018c2ecf20Sopenharmony_ci		      struct drm_rect *clip, bool swap)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct drm_gem_object *gem = drm_gem_fb_get_obj(fb, 0);
2048c2ecf20Sopenharmony_ci	struct drm_gem_cma_object *cma_obj = to_drm_gem_cma_obj(gem);
2058c2ecf20Sopenharmony_ci	struct dma_buf_attachment *import_attach = gem->import_attach;
2068c2ecf20Sopenharmony_ci	struct drm_format_name_buf format_name;
2078c2ecf20Sopenharmony_ci	void *src = cma_obj->vaddr;
2088c2ecf20Sopenharmony_ci	int ret = 0;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	if (import_attach) {
2118c2ecf20Sopenharmony_ci		ret = dma_buf_begin_cpu_access(import_attach->dmabuf,
2128c2ecf20Sopenharmony_ci					       DMA_FROM_DEVICE);
2138c2ecf20Sopenharmony_ci		if (ret)
2148c2ecf20Sopenharmony_ci			return ret;
2158c2ecf20Sopenharmony_ci	}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ci	switch (fb->format->format) {
2188c2ecf20Sopenharmony_ci	case DRM_FORMAT_RGB565:
2198c2ecf20Sopenharmony_ci		if (swap)
2208c2ecf20Sopenharmony_ci			drm_fb_swab(dst, src, fb, clip, !import_attach);
2218c2ecf20Sopenharmony_ci		else
2228c2ecf20Sopenharmony_ci			drm_fb_memcpy(dst, src, fb, clip);
2238c2ecf20Sopenharmony_ci		break;
2248c2ecf20Sopenharmony_ci	case DRM_FORMAT_XRGB8888:
2258c2ecf20Sopenharmony_ci		drm_fb_xrgb8888_to_rgb565(dst, src, fb, clip, swap);
2268c2ecf20Sopenharmony_ci		break;
2278c2ecf20Sopenharmony_ci	default:
2288c2ecf20Sopenharmony_ci		drm_err_once(fb->dev, "Format is not supported: %s\n",
2298c2ecf20Sopenharmony_ci			     drm_get_format_name(fb->format->format, &format_name));
2308c2ecf20Sopenharmony_ci		return -EINVAL;
2318c2ecf20Sopenharmony_ci	}
2328c2ecf20Sopenharmony_ci
2338c2ecf20Sopenharmony_ci	if (import_attach)
2348c2ecf20Sopenharmony_ci		ret = dma_buf_end_cpu_access(import_attach->dmabuf,
2358c2ecf20Sopenharmony_ci					     DMA_FROM_DEVICE);
2368c2ecf20Sopenharmony_ci	return ret;
2378c2ecf20Sopenharmony_ci}
2388c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mipi_dbi_buf_copy);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_cistatic void mipi_dbi_set_window_address(struct mipi_dbi_dev *dbidev,
2418c2ecf20Sopenharmony_ci					unsigned int xs, unsigned int xe,
2428c2ecf20Sopenharmony_ci					unsigned int ys, unsigned int ye)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	struct mipi_dbi *dbi = &dbidev->dbi;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	xs += dbidev->left_offset;
2478c2ecf20Sopenharmony_ci	xe += dbidev->left_offset;
2488c2ecf20Sopenharmony_ci	ys += dbidev->top_offset;
2498c2ecf20Sopenharmony_ci	ye += dbidev->top_offset;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	mipi_dbi_command(dbi, MIPI_DCS_SET_COLUMN_ADDRESS, (xs >> 8) & 0xff,
2528c2ecf20Sopenharmony_ci			 xs & 0xff, (xe >> 8) & 0xff, xe & 0xff);
2538c2ecf20Sopenharmony_ci	mipi_dbi_command(dbi, MIPI_DCS_SET_PAGE_ADDRESS, (ys >> 8) & 0xff,
2548c2ecf20Sopenharmony_ci			 ys & 0xff, (ye >> 8) & 0xff, ye & 0xff);
2558c2ecf20Sopenharmony_ci}
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_cistatic void mipi_dbi_fb_dirty(struct drm_framebuffer *fb, struct drm_rect *rect)
2588c2ecf20Sopenharmony_ci{
2598c2ecf20Sopenharmony_ci	struct drm_gem_object *gem = drm_gem_fb_get_obj(fb, 0);
2608c2ecf20Sopenharmony_ci	struct drm_gem_cma_object *cma_obj = to_drm_gem_cma_obj(gem);
2618c2ecf20Sopenharmony_ci	struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(fb->dev);
2628c2ecf20Sopenharmony_ci	unsigned int height = rect->y2 - rect->y1;
2638c2ecf20Sopenharmony_ci	unsigned int width = rect->x2 - rect->x1;
2648c2ecf20Sopenharmony_ci	struct mipi_dbi *dbi = &dbidev->dbi;
2658c2ecf20Sopenharmony_ci	bool swap = dbi->swap_bytes;
2668c2ecf20Sopenharmony_ci	int idx, ret = 0;
2678c2ecf20Sopenharmony_ci	bool full;
2688c2ecf20Sopenharmony_ci	void *tr;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	if (WARN_ON(!fb))
2718c2ecf20Sopenharmony_ci		return;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci	if (!drm_dev_enter(fb->dev, &idx))
2748c2ecf20Sopenharmony_ci		return;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	full = width == fb->width && height == fb->height;
2778c2ecf20Sopenharmony_ci
2788c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("Flushing [FB:%d] " DRM_RECT_FMT "\n", fb->base.id, DRM_RECT_ARG(rect));
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	if (!dbi->dc || !full || swap ||
2818c2ecf20Sopenharmony_ci	    fb->format->format == DRM_FORMAT_XRGB8888) {
2828c2ecf20Sopenharmony_ci		tr = dbidev->tx_buf;
2838c2ecf20Sopenharmony_ci		ret = mipi_dbi_buf_copy(dbidev->tx_buf, fb, rect, swap);
2848c2ecf20Sopenharmony_ci		if (ret)
2858c2ecf20Sopenharmony_ci			goto err_msg;
2868c2ecf20Sopenharmony_ci	} else {
2878c2ecf20Sopenharmony_ci		tr = cma_obj->vaddr;
2888c2ecf20Sopenharmony_ci	}
2898c2ecf20Sopenharmony_ci
2908c2ecf20Sopenharmony_ci	mipi_dbi_set_window_address(dbidev, rect->x1, rect->x2 - 1, rect->y1,
2918c2ecf20Sopenharmony_ci				    rect->y2 - 1);
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	ret = mipi_dbi_command_buf(dbi, MIPI_DCS_WRITE_MEMORY_START, tr,
2948c2ecf20Sopenharmony_ci				   width * height * 2);
2958c2ecf20Sopenharmony_cierr_msg:
2968c2ecf20Sopenharmony_ci	if (ret)
2978c2ecf20Sopenharmony_ci		drm_err_once(fb->dev, "Failed to update display %d\n", ret);
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ci	drm_dev_exit(idx);
3008c2ecf20Sopenharmony_ci}
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci/**
3038c2ecf20Sopenharmony_ci * mipi_dbi_pipe_update - Display pipe update helper
3048c2ecf20Sopenharmony_ci * @pipe: Simple display pipe
3058c2ecf20Sopenharmony_ci * @old_state: Old plane state
3068c2ecf20Sopenharmony_ci *
3078c2ecf20Sopenharmony_ci * This function handles framebuffer flushing and vblank events. Drivers can use
3088c2ecf20Sopenharmony_ci * this as their &drm_simple_display_pipe_funcs->update callback.
3098c2ecf20Sopenharmony_ci */
3108c2ecf20Sopenharmony_civoid mipi_dbi_pipe_update(struct drm_simple_display_pipe *pipe,
3118c2ecf20Sopenharmony_ci			  struct drm_plane_state *old_state)
3128c2ecf20Sopenharmony_ci{
3138c2ecf20Sopenharmony_ci	struct drm_plane_state *state = pipe->plane.state;
3148c2ecf20Sopenharmony_ci	struct drm_rect rect;
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	if (!pipe->crtc.state->active)
3178c2ecf20Sopenharmony_ci		return;
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci	if (drm_atomic_helper_damage_merged(old_state, state, &rect))
3208c2ecf20Sopenharmony_ci		mipi_dbi_fb_dirty(state->fb, &rect);
3218c2ecf20Sopenharmony_ci}
3228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mipi_dbi_pipe_update);
3238c2ecf20Sopenharmony_ci
3248c2ecf20Sopenharmony_ci/**
3258c2ecf20Sopenharmony_ci * mipi_dbi_enable_flush - MIPI DBI enable helper
3268c2ecf20Sopenharmony_ci * @dbidev: MIPI DBI device structure
3278c2ecf20Sopenharmony_ci * @crtc_state: CRTC state
3288c2ecf20Sopenharmony_ci * @plane_state: Plane state
3298c2ecf20Sopenharmony_ci *
3308c2ecf20Sopenharmony_ci * Flushes the whole framebuffer and enables the backlight. Drivers can use this
3318c2ecf20Sopenharmony_ci * in their &drm_simple_display_pipe_funcs->enable callback.
3328c2ecf20Sopenharmony_ci *
3338c2ecf20Sopenharmony_ci * Note: Drivers which don't use mipi_dbi_pipe_update() because they have custom
3348c2ecf20Sopenharmony_ci * framebuffer flushing, can't use this function since they both use the same
3358c2ecf20Sopenharmony_ci * flushing code.
3368c2ecf20Sopenharmony_ci */
3378c2ecf20Sopenharmony_civoid mipi_dbi_enable_flush(struct mipi_dbi_dev *dbidev,
3388c2ecf20Sopenharmony_ci			   struct drm_crtc_state *crtc_state,
3398c2ecf20Sopenharmony_ci			   struct drm_plane_state *plane_state)
3408c2ecf20Sopenharmony_ci{
3418c2ecf20Sopenharmony_ci	struct drm_framebuffer *fb = plane_state->fb;
3428c2ecf20Sopenharmony_ci	struct drm_rect rect = {
3438c2ecf20Sopenharmony_ci		.x1 = 0,
3448c2ecf20Sopenharmony_ci		.x2 = fb->width,
3458c2ecf20Sopenharmony_ci		.y1 = 0,
3468c2ecf20Sopenharmony_ci		.y2 = fb->height,
3478c2ecf20Sopenharmony_ci	};
3488c2ecf20Sopenharmony_ci	int idx;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (!drm_dev_enter(&dbidev->drm, &idx))
3518c2ecf20Sopenharmony_ci		return;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci	mipi_dbi_fb_dirty(fb, &rect);
3548c2ecf20Sopenharmony_ci	backlight_enable(dbidev->backlight);
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	drm_dev_exit(idx);
3578c2ecf20Sopenharmony_ci}
3588c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mipi_dbi_enable_flush);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_cistatic void mipi_dbi_blank(struct mipi_dbi_dev *dbidev)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	struct drm_device *drm = &dbidev->drm;
3638c2ecf20Sopenharmony_ci	u16 height = drm->mode_config.min_height;
3648c2ecf20Sopenharmony_ci	u16 width = drm->mode_config.min_width;
3658c2ecf20Sopenharmony_ci	struct mipi_dbi *dbi = &dbidev->dbi;
3668c2ecf20Sopenharmony_ci	size_t len = width * height * 2;
3678c2ecf20Sopenharmony_ci	int idx;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	if (!drm_dev_enter(drm, &idx))
3708c2ecf20Sopenharmony_ci		return;
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	memset(dbidev->tx_buf, 0, len);
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	mipi_dbi_set_window_address(dbidev, 0, width - 1, 0, height - 1);
3758c2ecf20Sopenharmony_ci	mipi_dbi_command_buf(dbi, MIPI_DCS_WRITE_MEMORY_START,
3768c2ecf20Sopenharmony_ci			     (u8 *)dbidev->tx_buf, len);
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	drm_dev_exit(idx);
3798c2ecf20Sopenharmony_ci}
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci/**
3828c2ecf20Sopenharmony_ci * mipi_dbi_pipe_disable - MIPI DBI pipe disable helper
3838c2ecf20Sopenharmony_ci * @pipe: Display pipe
3848c2ecf20Sopenharmony_ci *
3858c2ecf20Sopenharmony_ci * This function disables backlight if present, if not the display memory is
3868c2ecf20Sopenharmony_ci * blanked. The regulator is disabled if in use. Drivers can use this as their
3878c2ecf20Sopenharmony_ci * &drm_simple_display_pipe_funcs->disable callback.
3888c2ecf20Sopenharmony_ci */
3898c2ecf20Sopenharmony_civoid mipi_dbi_pipe_disable(struct drm_simple_display_pipe *pipe)
3908c2ecf20Sopenharmony_ci{
3918c2ecf20Sopenharmony_ci	struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev);
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("\n");
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	if (dbidev->backlight)
3968c2ecf20Sopenharmony_ci		backlight_disable(dbidev->backlight);
3978c2ecf20Sopenharmony_ci	else
3988c2ecf20Sopenharmony_ci		mipi_dbi_blank(dbidev);
3998c2ecf20Sopenharmony_ci
4008c2ecf20Sopenharmony_ci	if (dbidev->regulator)
4018c2ecf20Sopenharmony_ci		regulator_disable(dbidev->regulator);
4028c2ecf20Sopenharmony_ci}
4038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mipi_dbi_pipe_disable);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_cistatic int mipi_dbi_connector_get_modes(struct drm_connector *connector)
4068c2ecf20Sopenharmony_ci{
4078c2ecf20Sopenharmony_ci	struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(connector->dev);
4088c2ecf20Sopenharmony_ci	struct drm_display_mode *mode;
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	mode = drm_mode_duplicate(connector->dev, &dbidev->mode);
4118c2ecf20Sopenharmony_ci	if (!mode) {
4128c2ecf20Sopenharmony_ci		DRM_ERROR("Failed to duplicate mode\n");
4138c2ecf20Sopenharmony_ci		return 0;
4148c2ecf20Sopenharmony_ci	}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_ci	if (mode->name[0] == '\0')
4178c2ecf20Sopenharmony_ci		drm_mode_set_name(mode);
4188c2ecf20Sopenharmony_ci
4198c2ecf20Sopenharmony_ci	mode->type |= DRM_MODE_TYPE_PREFERRED;
4208c2ecf20Sopenharmony_ci	drm_mode_probed_add(connector, mode);
4218c2ecf20Sopenharmony_ci
4228c2ecf20Sopenharmony_ci	if (mode->width_mm) {
4238c2ecf20Sopenharmony_ci		connector->display_info.width_mm = mode->width_mm;
4248c2ecf20Sopenharmony_ci		connector->display_info.height_mm = mode->height_mm;
4258c2ecf20Sopenharmony_ci	}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	return 1;
4288c2ecf20Sopenharmony_ci}
4298c2ecf20Sopenharmony_ci
4308c2ecf20Sopenharmony_cistatic const struct drm_connector_helper_funcs mipi_dbi_connector_hfuncs = {
4318c2ecf20Sopenharmony_ci	.get_modes = mipi_dbi_connector_get_modes,
4328c2ecf20Sopenharmony_ci};
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_cistatic const struct drm_connector_funcs mipi_dbi_connector_funcs = {
4358c2ecf20Sopenharmony_ci	.reset = drm_atomic_helper_connector_reset,
4368c2ecf20Sopenharmony_ci	.fill_modes = drm_helper_probe_single_connector_modes,
4378c2ecf20Sopenharmony_ci	.destroy = drm_connector_cleanup,
4388c2ecf20Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
4398c2ecf20Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
4408c2ecf20Sopenharmony_ci};
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_cistatic int mipi_dbi_rotate_mode(struct drm_display_mode *mode,
4438c2ecf20Sopenharmony_ci				unsigned int rotation)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	if (rotation == 0 || rotation == 180) {
4468c2ecf20Sopenharmony_ci		return 0;
4478c2ecf20Sopenharmony_ci	} else if (rotation == 90 || rotation == 270) {
4488c2ecf20Sopenharmony_ci		swap(mode->hdisplay, mode->vdisplay);
4498c2ecf20Sopenharmony_ci		swap(mode->hsync_start, mode->vsync_start);
4508c2ecf20Sopenharmony_ci		swap(mode->hsync_end, mode->vsync_end);
4518c2ecf20Sopenharmony_ci		swap(mode->htotal, mode->vtotal);
4528c2ecf20Sopenharmony_ci		swap(mode->width_mm, mode->height_mm);
4538c2ecf20Sopenharmony_ci		return 0;
4548c2ecf20Sopenharmony_ci	} else {
4558c2ecf20Sopenharmony_ci		return -EINVAL;
4568c2ecf20Sopenharmony_ci	}
4578c2ecf20Sopenharmony_ci}
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_cistatic const struct drm_mode_config_funcs mipi_dbi_mode_config_funcs = {
4608c2ecf20Sopenharmony_ci	.fb_create = drm_gem_fb_create_with_dirty,
4618c2ecf20Sopenharmony_ci	.atomic_check = drm_atomic_helper_check,
4628c2ecf20Sopenharmony_ci	.atomic_commit = drm_atomic_helper_commit,
4638c2ecf20Sopenharmony_ci};
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_cistatic const uint32_t mipi_dbi_formats[] = {
4668c2ecf20Sopenharmony_ci	DRM_FORMAT_RGB565,
4678c2ecf20Sopenharmony_ci	DRM_FORMAT_XRGB8888,
4688c2ecf20Sopenharmony_ci};
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci/**
4718c2ecf20Sopenharmony_ci * mipi_dbi_dev_init_with_formats - MIPI DBI device initialization with custom formats
4728c2ecf20Sopenharmony_ci * @dbidev: MIPI DBI device structure to initialize
4738c2ecf20Sopenharmony_ci * @funcs: Display pipe functions
4748c2ecf20Sopenharmony_ci * @formats: Array of supported formats (DRM_FORMAT\_\*).
4758c2ecf20Sopenharmony_ci * @format_count: Number of elements in @formats
4768c2ecf20Sopenharmony_ci * @mode: Display mode
4778c2ecf20Sopenharmony_ci * @rotation: Initial rotation in degrees Counter Clock Wise
4788c2ecf20Sopenharmony_ci * @tx_buf_size: Allocate a transmit buffer of this size.
4798c2ecf20Sopenharmony_ci *
4808c2ecf20Sopenharmony_ci * This function sets up a &drm_simple_display_pipe with a &drm_connector that
4818c2ecf20Sopenharmony_ci * has one fixed &drm_display_mode which is rotated according to @rotation.
4828c2ecf20Sopenharmony_ci * This mode is used to set the mode config min/max width/height properties.
4838c2ecf20Sopenharmony_ci *
4848c2ecf20Sopenharmony_ci * Use mipi_dbi_dev_init() if you don't need custom formats.
4858c2ecf20Sopenharmony_ci *
4868c2ecf20Sopenharmony_ci * Note:
4878c2ecf20Sopenharmony_ci * Some of the helper functions expects RGB565 to be the default format and the
4888c2ecf20Sopenharmony_ci * transmit buffer sized to fit that.
4898c2ecf20Sopenharmony_ci *
4908c2ecf20Sopenharmony_ci * Returns:
4918c2ecf20Sopenharmony_ci * Zero on success, negative error code on failure.
4928c2ecf20Sopenharmony_ci */
4938c2ecf20Sopenharmony_ciint mipi_dbi_dev_init_with_formats(struct mipi_dbi_dev *dbidev,
4948c2ecf20Sopenharmony_ci				   const struct drm_simple_display_pipe_funcs *funcs,
4958c2ecf20Sopenharmony_ci				   const uint32_t *formats, unsigned int format_count,
4968c2ecf20Sopenharmony_ci				   const struct drm_display_mode *mode,
4978c2ecf20Sopenharmony_ci				   unsigned int rotation, size_t tx_buf_size)
4988c2ecf20Sopenharmony_ci{
4998c2ecf20Sopenharmony_ci	static const uint64_t modifiers[] = {
5008c2ecf20Sopenharmony_ci		DRM_FORMAT_MOD_LINEAR,
5018c2ecf20Sopenharmony_ci		DRM_FORMAT_MOD_INVALID
5028c2ecf20Sopenharmony_ci	};
5038c2ecf20Sopenharmony_ci	struct drm_device *drm = &dbidev->drm;
5048c2ecf20Sopenharmony_ci	int ret;
5058c2ecf20Sopenharmony_ci
5068c2ecf20Sopenharmony_ci	if (!dbidev->dbi.command)
5078c2ecf20Sopenharmony_ci		return -EINVAL;
5088c2ecf20Sopenharmony_ci
5098c2ecf20Sopenharmony_ci	ret = drmm_mode_config_init(drm);
5108c2ecf20Sopenharmony_ci	if (ret)
5118c2ecf20Sopenharmony_ci		return ret;
5128c2ecf20Sopenharmony_ci
5138c2ecf20Sopenharmony_ci	dbidev->tx_buf = devm_kmalloc(drm->dev, tx_buf_size, GFP_KERNEL);
5148c2ecf20Sopenharmony_ci	if (!dbidev->tx_buf)
5158c2ecf20Sopenharmony_ci		return -ENOMEM;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci	drm_mode_copy(&dbidev->mode, mode);
5188c2ecf20Sopenharmony_ci	ret = mipi_dbi_rotate_mode(&dbidev->mode, rotation);
5198c2ecf20Sopenharmony_ci	if (ret) {
5208c2ecf20Sopenharmony_ci		DRM_ERROR("Illegal rotation value %u\n", rotation);
5218c2ecf20Sopenharmony_ci		return -EINVAL;
5228c2ecf20Sopenharmony_ci	}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci	drm_connector_helper_add(&dbidev->connector, &mipi_dbi_connector_hfuncs);
5258c2ecf20Sopenharmony_ci	ret = drm_connector_init(drm, &dbidev->connector, &mipi_dbi_connector_funcs,
5268c2ecf20Sopenharmony_ci				 DRM_MODE_CONNECTOR_SPI);
5278c2ecf20Sopenharmony_ci	if (ret)
5288c2ecf20Sopenharmony_ci		return ret;
5298c2ecf20Sopenharmony_ci
5308c2ecf20Sopenharmony_ci	ret = drm_simple_display_pipe_init(drm, &dbidev->pipe, funcs, formats, format_count,
5318c2ecf20Sopenharmony_ci					   modifiers, &dbidev->connector);
5328c2ecf20Sopenharmony_ci	if (ret)
5338c2ecf20Sopenharmony_ci		return ret;
5348c2ecf20Sopenharmony_ci
5358c2ecf20Sopenharmony_ci	drm_plane_enable_fb_damage_clips(&dbidev->pipe.plane);
5368c2ecf20Sopenharmony_ci
5378c2ecf20Sopenharmony_ci	drm->mode_config.funcs = &mipi_dbi_mode_config_funcs;
5388c2ecf20Sopenharmony_ci	drm->mode_config.min_width = dbidev->mode.hdisplay;
5398c2ecf20Sopenharmony_ci	drm->mode_config.max_width = dbidev->mode.hdisplay;
5408c2ecf20Sopenharmony_ci	drm->mode_config.min_height = dbidev->mode.vdisplay;
5418c2ecf20Sopenharmony_ci	drm->mode_config.max_height = dbidev->mode.vdisplay;
5428c2ecf20Sopenharmony_ci	dbidev->rotation = rotation;
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci	DRM_DEBUG_KMS("rotation = %u\n", rotation);
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	return 0;
5478c2ecf20Sopenharmony_ci}
5488c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mipi_dbi_dev_init_with_formats);
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_ci/**
5518c2ecf20Sopenharmony_ci * mipi_dbi_dev_init - MIPI DBI device initialization
5528c2ecf20Sopenharmony_ci * @dbidev: MIPI DBI device structure to initialize
5538c2ecf20Sopenharmony_ci * @funcs: Display pipe functions
5548c2ecf20Sopenharmony_ci * @mode: Display mode
5558c2ecf20Sopenharmony_ci * @rotation: Initial rotation in degrees Counter Clock Wise
5568c2ecf20Sopenharmony_ci *
5578c2ecf20Sopenharmony_ci * This function sets up a &drm_simple_display_pipe with a &drm_connector that
5588c2ecf20Sopenharmony_ci * has one fixed &drm_display_mode which is rotated according to @rotation.
5598c2ecf20Sopenharmony_ci * This mode is used to set the mode config min/max width/height properties.
5608c2ecf20Sopenharmony_ci * Additionally &mipi_dbi.tx_buf is allocated.
5618c2ecf20Sopenharmony_ci *
5628c2ecf20Sopenharmony_ci * Supported formats: Native RGB565 and emulated XRGB8888.
5638c2ecf20Sopenharmony_ci *
5648c2ecf20Sopenharmony_ci * Returns:
5658c2ecf20Sopenharmony_ci * Zero on success, negative error code on failure.
5668c2ecf20Sopenharmony_ci */
5678c2ecf20Sopenharmony_ciint mipi_dbi_dev_init(struct mipi_dbi_dev *dbidev,
5688c2ecf20Sopenharmony_ci		      const struct drm_simple_display_pipe_funcs *funcs,
5698c2ecf20Sopenharmony_ci		      const struct drm_display_mode *mode, unsigned int rotation)
5708c2ecf20Sopenharmony_ci{
5718c2ecf20Sopenharmony_ci	size_t bufsize = mode->vdisplay * mode->hdisplay * sizeof(u16);
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	dbidev->drm.mode_config.preferred_depth = 16;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	return mipi_dbi_dev_init_with_formats(dbidev, funcs, mipi_dbi_formats,
5768c2ecf20Sopenharmony_ci					      ARRAY_SIZE(mipi_dbi_formats), mode,
5778c2ecf20Sopenharmony_ci					      rotation, bufsize);
5788c2ecf20Sopenharmony_ci}
5798c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mipi_dbi_dev_init);
5808c2ecf20Sopenharmony_ci
5818c2ecf20Sopenharmony_ci/**
5828c2ecf20Sopenharmony_ci * mipi_dbi_hw_reset - Hardware reset of controller
5838c2ecf20Sopenharmony_ci * @dbi: MIPI DBI structure
5848c2ecf20Sopenharmony_ci *
5858c2ecf20Sopenharmony_ci * Reset controller if the &mipi_dbi->reset gpio is set.
5868c2ecf20Sopenharmony_ci */
5878c2ecf20Sopenharmony_civoid mipi_dbi_hw_reset(struct mipi_dbi *dbi)
5888c2ecf20Sopenharmony_ci{
5898c2ecf20Sopenharmony_ci	if (!dbi->reset)
5908c2ecf20Sopenharmony_ci		return;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(dbi->reset, 0);
5938c2ecf20Sopenharmony_ci	usleep_range(20, 1000);
5948c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(dbi->reset, 1);
5958c2ecf20Sopenharmony_ci	msleep(120);
5968c2ecf20Sopenharmony_ci}
5978c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mipi_dbi_hw_reset);
5988c2ecf20Sopenharmony_ci
5998c2ecf20Sopenharmony_ci/**
6008c2ecf20Sopenharmony_ci * mipi_dbi_display_is_on - Check if display is on
6018c2ecf20Sopenharmony_ci * @dbi: MIPI DBI structure
6028c2ecf20Sopenharmony_ci *
6038c2ecf20Sopenharmony_ci * This function checks the Power Mode register (if readable) to see if
6048c2ecf20Sopenharmony_ci * display output is turned on. This can be used to see if the bootloader
6058c2ecf20Sopenharmony_ci * has already turned on the display avoiding flicker when the pipeline is
6068c2ecf20Sopenharmony_ci * enabled.
6078c2ecf20Sopenharmony_ci *
6088c2ecf20Sopenharmony_ci * Returns:
6098c2ecf20Sopenharmony_ci * true if the display can be verified to be on, false otherwise.
6108c2ecf20Sopenharmony_ci */
6118c2ecf20Sopenharmony_cibool mipi_dbi_display_is_on(struct mipi_dbi *dbi)
6128c2ecf20Sopenharmony_ci{
6138c2ecf20Sopenharmony_ci	u8 val;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	if (mipi_dbi_command_read(dbi, MIPI_DCS_GET_POWER_MODE, &val))
6168c2ecf20Sopenharmony_ci		return false;
6178c2ecf20Sopenharmony_ci
6188c2ecf20Sopenharmony_ci	val &= ~DCS_POWER_MODE_RESERVED_MASK;
6198c2ecf20Sopenharmony_ci
6208c2ecf20Sopenharmony_ci	/* The poweron/reset value is 08h DCS_POWER_MODE_DISPLAY_NORMAL_MODE */
6218c2ecf20Sopenharmony_ci	if (val != (DCS_POWER_MODE_DISPLAY |
6228c2ecf20Sopenharmony_ci	    DCS_POWER_MODE_DISPLAY_NORMAL_MODE | DCS_POWER_MODE_SLEEP_MODE))
6238c2ecf20Sopenharmony_ci		return false;
6248c2ecf20Sopenharmony_ci
6258c2ecf20Sopenharmony_ci	DRM_DEBUG_DRIVER("Display is ON\n");
6268c2ecf20Sopenharmony_ci
6278c2ecf20Sopenharmony_ci	return true;
6288c2ecf20Sopenharmony_ci}
6298c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mipi_dbi_display_is_on);
6308c2ecf20Sopenharmony_ci
6318c2ecf20Sopenharmony_cistatic int mipi_dbi_poweron_reset_conditional(struct mipi_dbi_dev *dbidev, bool cond)
6328c2ecf20Sopenharmony_ci{
6338c2ecf20Sopenharmony_ci	struct device *dev = dbidev->drm.dev;
6348c2ecf20Sopenharmony_ci	struct mipi_dbi *dbi = &dbidev->dbi;
6358c2ecf20Sopenharmony_ci	int ret;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	if (dbidev->regulator) {
6388c2ecf20Sopenharmony_ci		ret = regulator_enable(dbidev->regulator);
6398c2ecf20Sopenharmony_ci		if (ret) {
6408c2ecf20Sopenharmony_ci			DRM_DEV_ERROR(dev, "Failed to enable regulator (%d)\n", ret);
6418c2ecf20Sopenharmony_ci			return ret;
6428c2ecf20Sopenharmony_ci		}
6438c2ecf20Sopenharmony_ci	}
6448c2ecf20Sopenharmony_ci
6458c2ecf20Sopenharmony_ci	if (cond && mipi_dbi_display_is_on(dbi))
6468c2ecf20Sopenharmony_ci		return 1;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	mipi_dbi_hw_reset(dbi);
6498c2ecf20Sopenharmony_ci	ret = mipi_dbi_command(dbi, MIPI_DCS_SOFT_RESET);
6508c2ecf20Sopenharmony_ci	if (ret) {
6518c2ecf20Sopenharmony_ci		DRM_DEV_ERROR(dev, "Failed to send reset command (%d)\n", ret);
6528c2ecf20Sopenharmony_ci		if (dbidev->regulator)
6538c2ecf20Sopenharmony_ci			regulator_disable(dbidev->regulator);
6548c2ecf20Sopenharmony_ci		return ret;
6558c2ecf20Sopenharmony_ci	}
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	/*
6588c2ecf20Sopenharmony_ci	 * If we did a hw reset, we know the controller is in Sleep mode and
6598c2ecf20Sopenharmony_ci	 * per MIPI DSC spec should wait 5ms after soft reset. If we didn't,
6608c2ecf20Sopenharmony_ci	 * we assume worst case and wait 120ms.
6618c2ecf20Sopenharmony_ci	 */
6628c2ecf20Sopenharmony_ci	if (dbi->reset)
6638c2ecf20Sopenharmony_ci		usleep_range(5000, 20000);
6648c2ecf20Sopenharmony_ci	else
6658c2ecf20Sopenharmony_ci		msleep(120);
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	return 0;
6688c2ecf20Sopenharmony_ci}
6698c2ecf20Sopenharmony_ci
6708c2ecf20Sopenharmony_ci/**
6718c2ecf20Sopenharmony_ci * mipi_dbi_poweron_reset - MIPI DBI poweron and reset
6728c2ecf20Sopenharmony_ci * @dbidev: MIPI DBI device structure
6738c2ecf20Sopenharmony_ci *
6748c2ecf20Sopenharmony_ci * This function enables the regulator if used and does a hardware and software
6758c2ecf20Sopenharmony_ci * reset.
6768c2ecf20Sopenharmony_ci *
6778c2ecf20Sopenharmony_ci * Returns:
6788c2ecf20Sopenharmony_ci * Zero on success, or a negative error code.
6798c2ecf20Sopenharmony_ci */
6808c2ecf20Sopenharmony_ciint mipi_dbi_poweron_reset(struct mipi_dbi_dev *dbidev)
6818c2ecf20Sopenharmony_ci{
6828c2ecf20Sopenharmony_ci	return mipi_dbi_poweron_reset_conditional(dbidev, false);
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mipi_dbi_poweron_reset);
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci/**
6878c2ecf20Sopenharmony_ci * mipi_dbi_poweron_conditional_reset - MIPI DBI poweron and conditional reset
6888c2ecf20Sopenharmony_ci * @dbidev: MIPI DBI device structure
6898c2ecf20Sopenharmony_ci *
6908c2ecf20Sopenharmony_ci * This function enables the regulator if used and if the display is off, it
6918c2ecf20Sopenharmony_ci * does a hardware and software reset. If mipi_dbi_display_is_on() determines
6928c2ecf20Sopenharmony_ci * that the display is on, no reset is performed.
6938c2ecf20Sopenharmony_ci *
6948c2ecf20Sopenharmony_ci * Returns:
6958c2ecf20Sopenharmony_ci * Zero if the controller was reset, 1 if the display was already on, or a
6968c2ecf20Sopenharmony_ci * negative error code.
6978c2ecf20Sopenharmony_ci */
6988c2ecf20Sopenharmony_ciint mipi_dbi_poweron_conditional_reset(struct mipi_dbi_dev *dbidev)
6998c2ecf20Sopenharmony_ci{
7008c2ecf20Sopenharmony_ci	return mipi_dbi_poweron_reset_conditional(dbidev, true);
7018c2ecf20Sopenharmony_ci}
7028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mipi_dbi_poweron_conditional_reset);
7038c2ecf20Sopenharmony_ci
7048c2ecf20Sopenharmony_ci#if IS_ENABLED(CONFIG_SPI)
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci/**
7078c2ecf20Sopenharmony_ci * mipi_dbi_spi_cmd_max_speed - get the maximum SPI bus speed
7088c2ecf20Sopenharmony_ci * @spi: SPI device
7098c2ecf20Sopenharmony_ci * @len: The transfer buffer length.
7108c2ecf20Sopenharmony_ci *
7118c2ecf20Sopenharmony_ci * Many controllers have a max speed of 10MHz, but can be pushed way beyond
7128c2ecf20Sopenharmony_ci * that. Increase reliability by running pixel data at max speed and the rest
7138c2ecf20Sopenharmony_ci * at 10MHz, preventing transfer glitches from messing up the init settings.
7148c2ecf20Sopenharmony_ci */
7158c2ecf20Sopenharmony_ciu32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len)
7168c2ecf20Sopenharmony_ci{
7178c2ecf20Sopenharmony_ci	if (len > 64)
7188c2ecf20Sopenharmony_ci		return 0; /* use default */
7198c2ecf20Sopenharmony_ci
7208c2ecf20Sopenharmony_ci	return min_t(u32, 10000000, spi->max_speed_hz);
7218c2ecf20Sopenharmony_ci}
7228c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mipi_dbi_spi_cmd_max_speed);
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_cistatic bool mipi_dbi_machine_little_endian(void)
7258c2ecf20Sopenharmony_ci{
7268c2ecf20Sopenharmony_ci#if defined(__LITTLE_ENDIAN)
7278c2ecf20Sopenharmony_ci	return true;
7288c2ecf20Sopenharmony_ci#else
7298c2ecf20Sopenharmony_ci	return false;
7308c2ecf20Sopenharmony_ci#endif
7318c2ecf20Sopenharmony_ci}
7328c2ecf20Sopenharmony_ci
7338c2ecf20Sopenharmony_ci/*
7348c2ecf20Sopenharmony_ci * MIPI DBI Type C Option 1
7358c2ecf20Sopenharmony_ci *
7368c2ecf20Sopenharmony_ci * If the SPI controller doesn't have 9 bits per word support,
7378c2ecf20Sopenharmony_ci * use blocks of 9 bytes to send 8x 9-bit words using a 8-bit SPI transfer.
7388c2ecf20Sopenharmony_ci * Pad partial blocks with MIPI_DCS_NOP (zero).
7398c2ecf20Sopenharmony_ci * This is how the D/C bit (x) is added:
7408c2ecf20Sopenharmony_ci *     x7654321
7418c2ecf20Sopenharmony_ci *     0x765432
7428c2ecf20Sopenharmony_ci *     10x76543
7438c2ecf20Sopenharmony_ci *     210x7654
7448c2ecf20Sopenharmony_ci *     3210x765
7458c2ecf20Sopenharmony_ci *     43210x76
7468c2ecf20Sopenharmony_ci *     543210x7
7478c2ecf20Sopenharmony_ci *     6543210x
7488c2ecf20Sopenharmony_ci *     76543210
7498c2ecf20Sopenharmony_ci */
7508c2ecf20Sopenharmony_ci
7518c2ecf20Sopenharmony_cistatic int mipi_dbi_spi1e_transfer(struct mipi_dbi *dbi, int dc,
7528c2ecf20Sopenharmony_ci				   const void *buf, size_t len,
7538c2ecf20Sopenharmony_ci				   unsigned int bpw)
7548c2ecf20Sopenharmony_ci{
7558c2ecf20Sopenharmony_ci	bool swap_bytes = (bpw == 16 && mipi_dbi_machine_little_endian());
7568c2ecf20Sopenharmony_ci	size_t chunk, max_chunk = dbi->tx_buf9_len;
7578c2ecf20Sopenharmony_ci	struct spi_device *spi = dbi->spi;
7588c2ecf20Sopenharmony_ci	struct spi_transfer tr = {
7598c2ecf20Sopenharmony_ci		.tx_buf = dbi->tx_buf9,
7608c2ecf20Sopenharmony_ci		.bits_per_word = 8,
7618c2ecf20Sopenharmony_ci	};
7628c2ecf20Sopenharmony_ci	struct spi_message m;
7638c2ecf20Sopenharmony_ci	const u8 *src = buf;
7648c2ecf20Sopenharmony_ci	int i, ret;
7658c2ecf20Sopenharmony_ci	u8 *dst;
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	if (drm_debug_enabled(DRM_UT_DRIVER))
7688c2ecf20Sopenharmony_ci		pr_debug("[drm:%s] dc=%d, max_chunk=%zu, transfers:\n",
7698c2ecf20Sopenharmony_ci			 __func__, dc, max_chunk);
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ci	tr.speed_hz = mipi_dbi_spi_cmd_max_speed(spi, len);
7728c2ecf20Sopenharmony_ci	spi_message_init_with_transfers(&m, &tr, 1);
7738c2ecf20Sopenharmony_ci
7748c2ecf20Sopenharmony_ci	if (!dc) {
7758c2ecf20Sopenharmony_ci		if (WARN_ON_ONCE(len != 1))
7768c2ecf20Sopenharmony_ci			return -EINVAL;
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci		/* Command: pad no-op's (zeroes) at beginning of block */
7798c2ecf20Sopenharmony_ci		dst = dbi->tx_buf9;
7808c2ecf20Sopenharmony_ci		memset(dst, 0, 9);
7818c2ecf20Sopenharmony_ci		dst[8] = *src;
7828c2ecf20Sopenharmony_ci		tr.len = 9;
7838c2ecf20Sopenharmony_ci
7848c2ecf20Sopenharmony_ci		return spi_sync(spi, &m);
7858c2ecf20Sopenharmony_ci	}
7868c2ecf20Sopenharmony_ci
7878c2ecf20Sopenharmony_ci	/* max with room for adding one bit per byte */
7888c2ecf20Sopenharmony_ci	max_chunk = max_chunk / 9 * 8;
7898c2ecf20Sopenharmony_ci	/* but no bigger than len */
7908c2ecf20Sopenharmony_ci	max_chunk = min(max_chunk, len);
7918c2ecf20Sopenharmony_ci	/* 8 byte blocks */
7928c2ecf20Sopenharmony_ci	max_chunk = max_t(size_t, 8, max_chunk & ~0x7);
7938c2ecf20Sopenharmony_ci
7948c2ecf20Sopenharmony_ci	while (len) {
7958c2ecf20Sopenharmony_ci		size_t added = 0;
7968c2ecf20Sopenharmony_ci
7978c2ecf20Sopenharmony_ci		chunk = min(len, max_chunk);
7988c2ecf20Sopenharmony_ci		len -= chunk;
7998c2ecf20Sopenharmony_ci		dst = dbi->tx_buf9;
8008c2ecf20Sopenharmony_ci
8018c2ecf20Sopenharmony_ci		if (chunk < 8) {
8028c2ecf20Sopenharmony_ci			u8 val, carry = 0;
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci			/* Data: pad no-op's (zeroes) at end of block */
8058c2ecf20Sopenharmony_ci			memset(dst, 0, 9);
8068c2ecf20Sopenharmony_ci
8078c2ecf20Sopenharmony_ci			if (swap_bytes) {
8088c2ecf20Sopenharmony_ci				for (i = 1; i < (chunk + 1); i++) {
8098c2ecf20Sopenharmony_ci					val = src[1];
8108c2ecf20Sopenharmony_ci					*dst++ = carry | BIT(8 - i) | (val >> i);
8118c2ecf20Sopenharmony_ci					carry = val << (8 - i);
8128c2ecf20Sopenharmony_ci					i++;
8138c2ecf20Sopenharmony_ci					val = src[0];
8148c2ecf20Sopenharmony_ci					*dst++ = carry | BIT(8 - i) | (val >> i);
8158c2ecf20Sopenharmony_ci					carry = val << (8 - i);
8168c2ecf20Sopenharmony_ci					src += 2;
8178c2ecf20Sopenharmony_ci				}
8188c2ecf20Sopenharmony_ci				*dst++ = carry;
8198c2ecf20Sopenharmony_ci			} else {
8208c2ecf20Sopenharmony_ci				for (i = 1; i < (chunk + 1); i++) {
8218c2ecf20Sopenharmony_ci					val = *src++;
8228c2ecf20Sopenharmony_ci					*dst++ = carry | BIT(8 - i) | (val >> i);
8238c2ecf20Sopenharmony_ci					carry = val << (8 - i);
8248c2ecf20Sopenharmony_ci				}
8258c2ecf20Sopenharmony_ci				*dst++ = carry;
8268c2ecf20Sopenharmony_ci			}
8278c2ecf20Sopenharmony_ci
8288c2ecf20Sopenharmony_ci			chunk = 8;
8298c2ecf20Sopenharmony_ci			added = 1;
8308c2ecf20Sopenharmony_ci		} else {
8318c2ecf20Sopenharmony_ci			for (i = 0; i < chunk; i += 8) {
8328c2ecf20Sopenharmony_ci				if (swap_bytes) {
8338c2ecf20Sopenharmony_ci					*dst++ =                 BIT(7) | (src[1] >> 1);
8348c2ecf20Sopenharmony_ci					*dst++ = (src[1] << 7) | BIT(6) | (src[0] >> 2);
8358c2ecf20Sopenharmony_ci					*dst++ = (src[0] << 6) | BIT(5) | (src[3] >> 3);
8368c2ecf20Sopenharmony_ci					*dst++ = (src[3] << 5) | BIT(4) | (src[2] >> 4);
8378c2ecf20Sopenharmony_ci					*dst++ = (src[2] << 4) | BIT(3) | (src[5] >> 5);
8388c2ecf20Sopenharmony_ci					*dst++ = (src[5] << 3) | BIT(2) | (src[4] >> 6);
8398c2ecf20Sopenharmony_ci					*dst++ = (src[4] << 2) | BIT(1) | (src[7] >> 7);
8408c2ecf20Sopenharmony_ci					*dst++ = (src[7] << 1) | BIT(0);
8418c2ecf20Sopenharmony_ci					*dst++ = src[6];
8428c2ecf20Sopenharmony_ci				} else {
8438c2ecf20Sopenharmony_ci					*dst++ =                 BIT(7) | (src[0] >> 1);
8448c2ecf20Sopenharmony_ci					*dst++ = (src[0] << 7) | BIT(6) | (src[1] >> 2);
8458c2ecf20Sopenharmony_ci					*dst++ = (src[1] << 6) | BIT(5) | (src[2] >> 3);
8468c2ecf20Sopenharmony_ci					*dst++ = (src[2] << 5) | BIT(4) | (src[3] >> 4);
8478c2ecf20Sopenharmony_ci					*dst++ = (src[3] << 4) | BIT(3) | (src[4] >> 5);
8488c2ecf20Sopenharmony_ci					*dst++ = (src[4] << 3) | BIT(2) | (src[5] >> 6);
8498c2ecf20Sopenharmony_ci					*dst++ = (src[5] << 2) | BIT(1) | (src[6] >> 7);
8508c2ecf20Sopenharmony_ci					*dst++ = (src[6] << 1) | BIT(0);
8518c2ecf20Sopenharmony_ci					*dst++ = src[7];
8528c2ecf20Sopenharmony_ci				}
8538c2ecf20Sopenharmony_ci
8548c2ecf20Sopenharmony_ci				src += 8;
8558c2ecf20Sopenharmony_ci				added++;
8568c2ecf20Sopenharmony_ci			}
8578c2ecf20Sopenharmony_ci		}
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci		tr.len = chunk + added;
8608c2ecf20Sopenharmony_ci
8618c2ecf20Sopenharmony_ci		ret = spi_sync(spi, &m);
8628c2ecf20Sopenharmony_ci		if (ret)
8638c2ecf20Sopenharmony_ci			return ret;
8648c2ecf20Sopenharmony_ci	}
8658c2ecf20Sopenharmony_ci
8668c2ecf20Sopenharmony_ci	return 0;
8678c2ecf20Sopenharmony_ci}
8688c2ecf20Sopenharmony_ci
8698c2ecf20Sopenharmony_cistatic int mipi_dbi_spi1_transfer(struct mipi_dbi *dbi, int dc,
8708c2ecf20Sopenharmony_ci				  const void *buf, size_t len,
8718c2ecf20Sopenharmony_ci				  unsigned int bpw)
8728c2ecf20Sopenharmony_ci{
8738c2ecf20Sopenharmony_ci	struct spi_device *spi = dbi->spi;
8748c2ecf20Sopenharmony_ci	struct spi_transfer tr = {
8758c2ecf20Sopenharmony_ci		.bits_per_word = 9,
8768c2ecf20Sopenharmony_ci	};
8778c2ecf20Sopenharmony_ci	const u16 *src16 = buf;
8788c2ecf20Sopenharmony_ci	const u8 *src8 = buf;
8798c2ecf20Sopenharmony_ci	struct spi_message m;
8808c2ecf20Sopenharmony_ci	size_t max_chunk;
8818c2ecf20Sopenharmony_ci	u16 *dst16;
8828c2ecf20Sopenharmony_ci	int ret;
8838c2ecf20Sopenharmony_ci
8848c2ecf20Sopenharmony_ci	if (!spi_is_bpw_supported(spi, 9))
8858c2ecf20Sopenharmony_ci		return mipi_dbi_spi1e_transfer(dbi, dc, buf, len, bpw);
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	tr.speed_hz = mipi_dbi_spi_cmd_max_speed(spi, len);
8888c2ecf20Sopenharmony_ci	max_chunk = dbi->tx_buf9_len;
8898c2ecf20Sopenharmony_ci	dst16 = dbi->tx_buf9;
8908c2ecf20Sopenharmony_ci
8918c2ecf20Sopenharmony_ci	if (drm_debug_enabled(DRM_UT_DRIVER))
8928c2ecf20Sopenharmony_ci		pr_debug("[drm:%s] dc=%d, max_chunk=%zu, transfers:\n",
8938c2ecf20Sopenharmony_ci			 __func__, dc, max_chunk);
8948c2ecf20Sopenharmony_ci
8958c2ecf20Sopenharmony_ci	max_chunk = min(max_chunk / 2, len);
8968c2ecf20Sopenharmony_ci
8978c2ecf20Sopenharmony_ci	spi_message_init_with_transfers(&m, &tr, 1);
8988c2ecf20Sopenharmony_ci	tr.tx_buf = dst16;
8998c2ecf20Sopenharmony_ci
9008c2ecf20Sopenharmony_ci	while (len) {
9018c2ecf20Sopenharmony_ci		size_t chunk = min(len, max_chunk);
9028c2ecf20Sopenharmony_ci		unsigned int i;
9038c2ecf20Sopenharmony_ci
9048c2ecf20Sopenharmony_ci		if (bpw == 16 && mipi_dbi_machine_little_endian()) {
9058c2ecf20Sopenharmony_ci			for (i = 0; i < (chunk * 2); i += 2) {
9068c2ecf20Sopenharmony_ci				dst16[i]     = *src16 >> 8;
9078c2ecf20Sopenharmony_ci				dst16[i + 1] = *src16++ & 0xFF;
9088c2ecf20Sopenharmony_ci				if (dc) {
9098c2ecf20Sopenharmony_ci					dst16[i]     |= 0x0100;
9108c2ecf20Sopenharmony_ci					dst16[i + 1] |= 0x0100;
9118c2ecf20Sopenharmony_ci				}
9128c2ecf20Sopenharmony_ci			}
9138c2ecf20Sopenharmony_ci		} else {
9148c2ecf20Sopenharmony_ci			for (i = 0; i < chunk; i++) {
9158c2ecf20Sopenharmony_ci				dst16[i] = *src8++;
9168c2ecf20Sopenharmony_ci				if (dc)
9178c2ecf20Sopenharmony_ci					dst16[i] |= 0x0100;
9188c2ecf20Sopenharmony_ci			}
9198c2ecf20Sopenharmony_ci		}
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci		tr.len = chunk * 2;
9228c2ecf20Sopenharmony_ci		len -= chunk;
9238c2ecf20Sopenharmony_ci
9248c2ecf20Sopenharmony_ci		ret = spi_sync(spi, &m);
9258c2ecf20Sopenharmony_ci		if (ret)
9268c2ecf20Sopenharmony_ci			return ret;
9278c2ecf20Sopenharmony_ci	}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci	return 0;
9308c2ecf20Sopenharmony_ci}
9318c2ecf20Sopenharmony_ci
9328c2ecf20Sopenharmony_cistatic int mipi_dbi_typec1_command(struct mipi_dbi *dbi, u8 *cmd,
9338c2ecf20Sopenharmony_ci				   u8 *parameters, size_t num)
9348c2ecf20Sopenharmony_ci{
9358c2ecf20Sopenharmony_ci	unsigned int bpw = (*cmd == MIPI_DCS_WRITE_MEMORY_START) ? 16 : 8;
9368c2ecf20Sopenharmony_ci	int ret;
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci	if (mipi_dbi_command_is_read(dbi, *cmd))
9398c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
9408c2ecf20Sopenharmony_ci
9418c2ecf20Sopenharmony_ci	MIPI_DBI_DEBUG_COMMAND(*cmd, parameters, num);
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	ret = mipi_dbi_spi1_transfer(dbi, 0, cmd, 1, 8);
9448c2ecf20Sopenharmony_ci	if (ret || !num)
9458c2ecf20Sopenharmony_ci		return ret;
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci	return mipi_dbi_spi1_transfer(dbi, 1, parameters, num, bpw);
9488c2ecf20Sopenharmony_ci}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci/* MIPI DBI Type C Option 3 */
9518c2ecf20Sopenharmony_ci
9528c2ecf20Sopenharmony_cistatic int mipi_dbi_typec3_command_read(struct mipi_dbi *dbi, u8 *cmd,
9538c2ecf20Sopenharmony_ci					u8 *data, size_t len)
9548c2ecf20Sopenharmony_ci{
9558c2ecf20Sopenharmony_ci	struct spi_device *spi = dbi->spi;
9568c2ecf20Sopenharmony_ci	u32 speed_hz = min_t(u32, MIPI_DBI_MAX_SPI_READ_SPEED,
9578c2ecf20Sopenharmony_ci			     spi->max_speed_hz / 2);
9588c2ecf20Sopenharmony_ci	struct spi_transfer tr[2] = {
9598c2ecf20Sopenharmony_ci		{
9608c2ecf20Sopenharmony_ci			.speed_hz = speed_hz,
9618c2ecf20Sopenharmony_ci			.tx_buf = cmd,
9628c2ecf20Sopenharmony_ci			.len = 1,
9638c2ecf20Sopenharmony_ci		}, {
9648c2ecf20Sopenharmony_ci			.speed_hz = speed_hz,
9658c2ecf20Sopenharmony_ci			.len = len,
9668c2ecf20Sopenharmony_ci		},
9678c2ecf20Sopenharmony_ci	};
9688c2ecf20Sopenharmony_ci	struct spi_message m;
9698c2ecf20Sopenharmony_ci	u8 *buf;
9708c2ecf20Sopenharmony_ci	int ret;
9718c2ecf20Sopenharmony_ci
9728c2ecf20Sopenharmony_ci	if (!len)
9738c2ecf20Sopenharmony_ci		return -EINVAL;
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_ci	/*
9768c2ecf20Sopenharmony_ci	 * Support non-standard 24-bit and 32-bit Nokia read commands which
9778c2ecf20Sopenharmony_ci	 * start with a dummy clock, so we need to read an extra byte.
9788c2ecf20Sopenharmony_ci	 */
9798c2ecf20Sopenharmony_ci	if (*cmd == MIPI_DCS_GET_DISPLAY_ID ||
9808c2ecf20Sopenharmony_ci	    *cmd == MIPI_DCS_GET_DISPLAY_STATUS) {
9818c2ecf20Sopenharmony_ci		if (!(len == 3 || len == 4))
9828c2ecf20Sopenharmony_ci			return -EINVAL;
9838c2ecf20Sopenharmony_ci
9848c2ecf20Sopenharmony_ci		tr[1].len = len + 1;
9858c2ecf20Sopenharmony_ci	}
9868c2ecf20Sopenharmony_ci
9878c2ecf20Sopenharmony_ci	buf = kmalloc(tr[1].len, GFP_KERNEL);
9888c2ecf20Sopenharmony_ci	if (!buf)
9898c2ecf20Sopenharmony_ci		return -ENOMEM;
9908c2ecf20Sopenharmony_ci
9918c2ecf20Sopenharmony_ci	tr[1].rx_buf = buf;
9928c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(dbi->dc, 0);
9938c2ecf20Sopenharmony_ci
9948c2ecf20Sopenharmony_ci	spi_message_init_with_transfers(&m, tr, ARRAY_SIZE(tr));
9958c2ecf20Sopenharmony_ci	ret = spi_sync(spi, &m);
9968c2ecf20Sopenharmony_ci	if (ret)
9978c2ecf20Sopenharmony_ci		goto err_free;
9988c2ecf20Sopenharmony_ci
9998c2ecf20Sopenharmony_ci	if (tr[1].len == len) {
10008c2ecf20Sopenharmony_ci		memcpy(data, buf, len);
10018c2ecf20Sopenharmony_ci	} else {
10028c2ecf20Sopenharmony_ci		unsigned int i;
10038c2ecf20Sopenharmony_ci
10048c2ecf20Sopenharmony_ci		for (i = 0; i < len; i++)
10058c2ecf20Sopenharmony_ci			data[i] = (buf[i] << 1) | (buf[i + 1] >> 7);
10068c2ecf20Sopenharmony_ci	}
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	MIPI_DBI_DEBUG_COMMAND(*cmd, data, len);
10098c2ecf20Sopenharmony_ci
10108c2ecf20Sopenharmony_cierr_free:
10118c2ecf20Sopenharmony_ci	kfree(buf);
10128c2ecf20Sopenharmony_ci
10138c2ecf20Sopenharmony_ci	return ret;
10148c2ecf20Sopenharmony_ci}
10158c2ecf20Sopenharmony_ci
10168c2ecf20Sopenharmony_cistatic int mipi_dbi_typec3_command(struct mipi_dbi *dbi, u8 *cmd,
10178c2ecf20Sopenharmony_ci				   u8 *par, size_t num)
10188c2ecf20Sopenharmony_ci{
10198c2ecf20Sopenharmony_ci	struct spi_device *spi = dbi->spi;
10208c2ecf20Sopenharmony_ci	unsigned int bpw = 8;
10218c2ecf20Sopenharmony_ci	u32 speed_hz;
10228c2ecf20Sopenharmony_ci	int ret;
10238c2ecf20Sopenharmony_ci
10248c2ecf20Sopenharmony_ci	if (mipi_dbi_command_is_read(dbi, *cmd))
10258c2ecf20Sopenharmony_ci		return mipi_dbi_typec3_command_read(dbi, cmd, par, num);
10268c2ecf20Sopenharmony_ci
10278c2ecf20Sopenharmony_ci	MIPI_DBI_DEBUG_COMMAND(*cmd, par, num);
10288c2ecf20Sopenharmony_ci
10298c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(dbi->dc, 0);
10308c2ecf20Sopenharmony_ci	speed_hz = mipi_dbi_spi_cmd_max_speed(spi, 1);
10318c2ecf20Sopenharmony_ci	ret = mipi_dbi_spi_transfer(spi, speed_hz, 8, cmd, 1);
10328c2ecf20Sopenharmony_ci	if (ret || !num)
10338c2ecf20Sopenharmony_ci		return ret;
10348c2ecf20Sopenharmony_ci
10358c2ecf20Sopenharmony_ci	if (*cmd == MIPI_DCS_WRITE_MEMORY_START && !dbi->swap_bytes)
10368c2ecf20Sopenharmony_ci		bpw = 16;
10378c2ecf20Sopenharmony_ci
10388c2ecf20Sopenharmony_ci	gpiod_set_value_cansleep(dbi->dc, 1);
10398c2ecf20Sopenharmony_ci	speed_hz = mipi_dbi_spi_cmd_max_speed(spi, num);
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	return mipi_dbi_spi_transfer(spi, speed_hz, bpw, par, num);
10428c2ecf20Sopenharmony_ci}
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci/**
10458c2ecf20Sopenharmony_ci * mipi_dbi_spi_init - Initialize MIPI DBI SPI interface
10468c2ecf20Sopenharmony_ci * @spi: SPI device
10478c2ecf20Sopenharmony_ci * @dbi: MIPI DBI structure to initialize
10488c2ecf20Sopenharmony_ci * @dc: D/C gpio (optional)
10498c2ecf20Sopenharmony_ci *
10508c2ecf20Sopenharmony_ci * This function sets &mipi_dbi->command, enables &mipi_dbi->read_commands for the
10518c2ecf20Sopenharmony_ci * usual read commands. It should be followed by a call to mipi_dbi_dev_init() or
10528c2ecf20Sopenharmony_ci * a driver-specific init.
10538c2ecf20Sopenharmony_ci *
10548c2ecf20Sopenharmony_ci * If @dc is set, a Type C Option 3 interface is assumed, if not
10558c2ecf20Sopenharmony_ci * Type C Option 1.
10568c2ecf20Sopenharmony_ci *
10578c2ecf20Sopenharmony_ci * If the SPI master driver doesn't support the necessary bits per word,
10588c2ecf20Sopenharmony_ci * the following transformation is used:
10598c2ecf20Sopenharmony_ci *
10608c2ecf20Sopenharmony_ci * - 9-bit: reorder buffer as 9x 8-bit words, padded with no-op command.
10618c2ecf20Sopenharmony_ci * - 16-bit: if big endian send as 8-bit, if little endian swap bytes
10628c2ecf20Sopenharmony_ci *
10638c2ecf20Sopenharmony_ci * Returns:
10648c2ecf20Sopenharmony_ci * Zero on success, negative error code on failure.
10658c2ecf20Sopenharmony_ci */
10668c2ecf20Sopenharmony_ciint mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *dbi,
10678c2ecf20Sopenharmony_ci		      struct gpio_desc *dc)
10688c2ecf20Sopenharmony_ci{
10698c2ecf20Sopenharmony_ci	struct device *dev = &spi->dev;
10708c2ecf20Sopenharmony_ci	int ret;
10718c2ecf20Sopenharmony_ci
10728c2ecf20Sopenharmony_ci	/*
10738c2ecf20Sopenharmony_ci	 * Even though it's not the SPI device that does DMA (the master does),
10748c2ecf20Sopenharmony_ci	 * the dma mask is necessary for the dma_alloc_wc() in
10758c2ecf20Sopenharmony_ci	 * drm_gem_cma_create(). The dma_addr returned will be a physical
10768c2ecf20Sopenharmony_ci	 * address which might be different from the bus address, but this is
10778c2ecf20Sopenharmony_ci	 * not a problem since the address will not be used.
10788c2ecf20Sopenharmony_ci	 * The virtual address is used in the transfer and the SPI core
10798c2ecf20Sopenharmony_ci	 * re-maps it on the SPI master device using the DMA streaming API
10808c2ecf20Sopenharmony_ci	 * (spi_map_buf()).
10818c2ecf20Sopenharmony_ci	 */
10828c2ecf20Sopenharmony_ci	if (!dev->coherent_dma_mask) {
10838c2ecf20Sopenharmony_ci		ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
10848c2ecf20Sopenharmony_ci		if (ret) {
10858c2ecf20Sopenharmony_ci			dev_warn(dev, "Failed to set dma mask %d\n", ret);
10868c2ecf20Sopenharmony_ci			return ret;
10878c2ecf20Sopenharmony_ci		}
10888c2ecf20Sopenharmony_ci	}
10898c2ecf20Sopenharmony_ci
10908c2ecf20Sopenharmony_ci	dbi->spi = spi;
10918c2ecf20Sopenharmony_ci	dbi->read_commands = mipi_dbi_dcs_read_commands;
10928c2ecf20Sopenharmony_ci
10938c2ecf20Sopenharmony_ci	if (dc) {
10948c2ecf20Sopenharmony_ci		dbi->command = mipi_dbi_typec3_command;
10958c2ecf20Sopenharmony_ci		dbi->dc = dc;
10968c2ecf20Sopenharmony_ci		if (mipi_dbi_machine_little_endian() && !spi_is_bpw_supported(spi, 16))
10978c2ecf20Sopenharmony_ci			dbi->swap_bytes = true;
10988c2ecf20Sopenharmony_ci	} else {
10998c2ecf20Sopenharmony_ci		dbi->command = mipi_dbi_typec1_command;
11008c2ecf20Sopenharmony_ci		dbi->tx_buf9_len = SZ_16K;
11018c2ecf20Sopenharmony_ci		dbi->tx_buf9 = devm_kmalloc(dev, dbi->tx_buf9_len, GFP_KERNEL);
11028c2ecf20Sopenharmony_ci		if (!dbi->tx_buf9)
11038c2ecf20Sopenharmony_ci			return -ENOMEM;
11048c2ecf20Sopenharmony_ci	}
11058c2ecf20Sopenharmony_ci
11068c2ecf20Sopenharmony_ci	mutex_init(&dbi->cmdlock);
11078c2ecf20Sopenharmony_ci
11088c2ecf20Sopenharmony_ci	DRM_DEBUG_DRIVER("SPI speed: %uMHz\n", spi->max_speed_hz / 1000000);
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci	return 0;
11118c2ecf20Sopenharmony_ci}
11128c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mipi_dbi_spi_init);
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci/**
11158c2ecf20Sopenharmony_ci * mipi_dbi_spi_transfer - SPI transfer helper
11168c2ecf20Sopenharmony_ci * @spi: SPI device
11178c2ecf20Sopenharmony_ci * @speed_hz: Override speed (optional)
11188c2ecf20Sopenharmony_ci * @bpw: Bits per word
11198c2ecf20Sopenharmony_ci * @buf: Buffer to transfer
11208c2ecf20Sopenharmony_ci * @len: Buffer length
11218c2ecf20Sopenharmony_ci *
11228c2ecf20Sopenharmony_ci * This SPI transfer helper breaks up the transfer of @buf into chunks which
11238c2ecf20Sopenharmony_ci * the SPI controller driver can handle.
11248c2ecf20Sopenharmony_ci *
11258c2ecf20Sopenharmony_ci * Returns:
11268c2ecf20Sopenharmony_ci * Zero on success, negative error code on failure.
11278c2ecf20Sopenharmony_ci */
11288c2ecf20Sopenharmony_ciint mipi_dbi_spi_transfer(struct spi_device *spi, u32 speed_hz,
11298c2ecf20Sopenharmony_ci			  u8 bpw, const void *buf, size_t len)
11308c2ecf20Sopenharmony_ci{
11318c2ecf20Sopenharmony_ci	size_t max_chunk = spi_max_transfer_size(spi);
11328c2ecf20Sopenharmony_ci	struct spi_transfer tr = {
11338c2ecf20Sopenharmony_ci		.bits_per_word = bpw,
11348c2ecf20Sopenharmony_ci		.speed_hz = speed_hz,
11358c2ecf20Sopenharmony_ci	};
11368c2ecf20Sopenharmony_ci	struct spi_message m;
11378c2ecf20Sopenharmony_ci	size_t chunk;
11388c2ecf20Sopenharmony_ci	int ret;
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci	/* In __spi_validate, there's a validation that no partial transfers
11418c2ecf20Sopenharmony_ci	 * are accepted (xfer->len % w_size must be zero).
11428c2ecf20Sopenharmony_ci	 * Here we align max_chunk to multiple of 2 (16bits),
11438c2ecf20Sopenharmony_ci	 * to prevent transfers from being rejected.
11448c2ecf20Sopenharmony_ci	 */
11458c2ecf20Sopenharmony_ci	max_chunk = ALIGN_DOWN(max_chunk, 2);
11468c2ecf20Sopenharmony_ci
11478c2ecf20Sopenharmony_ci	spi_message_init_with_transfers(&m, &tr, 1);
11488c2ecf20Sopenharmony_ci
11498c2ecf20Sopenharmony_ci	while (len) {
11508c2ecf20Sopenharmony_ci		chunk = min(len, max_chunk);
11518c2ecf20Sopenharmony_ci
11528c2ecf20Sopenharmony_ci		tr.tx_buf = buf;
11538c2ecf20Sopenharmony_ci		tr.len = chunk;
11548c2ecf20Sopenharmony_ci		buf += chunk;
11558c2ecf20Sopenharmony_ci		len -= chunk;
11568c2ecf20Sopenharmony_ci
11578c2ecf20Sopenharmony_ci		ret = spi_sync(spi, &m);
11588c2ecf20Sopenharmony_ci		if (ret)
11598c2ecf20Sopenharmony_ci			return ret;
11608c2ecf20Sopenharmony_ci	}
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci	return 0;
11638c2ecf20Sopenharmony_ci}
11648c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mipi_dbi_spi_transfer);
11658c2ecf20Sopenharmony_ci
11668c2ecf20Sopenharmony_ci#endif /* CONFIG_SPI */
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_FS
11698c2ecf20Sopenharmony_ci
11708c2ecf20Sopenharmony_cistatic ssize_t mipi_dbi_debugfs_command_write(struct file *file,
11718c2ecf20Sopenharmony_ci					      const char __user *ubuf,
11728c2ecf20Sopenharmony_ci					      size_t count, loff_t *ppos)
11738c2ecf20Sopenharmony_ci{
11748c2ecf20Sopenharmony_ci	struct seq_file *m = file->private_data;
11758c2ecf20Sopenharmony_ci	struct mipi_dbi_dev *dbidev = m->private;
11768c2ecf20Sopenharmony_ci	u8 val, cmd = 0, parameters[64];
11778c2ecf20Sopenharmony_ci	char *buf, *pos, *token;
11788c2ecf20Sopenharmony_ci	int i, ret, idx;
11798c2ecf20Sopenharmony_ci
11808c2ecf20Sopenharmony_ci	if (!drm_dev_enter(&dbidev->drm, &idx))
11818c2ecf20Sopenharmony_ci		return -ENODEV;
11828c2ecf20Sopenharmony_ci
11838c2ecf20Sopenharmony_ci	buf = memdup_user_nul(ubuf, count);
11848c2ecf20Sopenharmony_ci	if (IS_ERR(buf)) {
11858c2ecf20Sopenharmony_ci		ret = PTR_ERR(buf);
11868c2ecf20Sopenharmony_ci		goto err_exit;
11878c2ecf20Sopenharmony_ci	}
11888c2ecf20Sopenharmony_ci
11898c2ecf20Sopenharmony_ci	/* strip trailing whitespace */
11908c2ecf20Sopenharmony_ci	for (i = count - 1; i > 0; i--)
11918c2ecf20Sopenharmony_ci		if (isspace(buf[i]))
11928c2ecf20Sopenharmony_ci			buf[i] = '\0';
11938c2ecf20Sopenharmony_ci		else
11948c2ecf20Sopenharmony_ci			break;
11958c2ecf20Sopenharmony_ci	i = 0;
11968c2ecf20Sopenharmony_ci	pos = buf;
11978c2ecf20Sopenharmony_ci	while (pos) {
11988c2ecf20Sopenharmony_ci		token = strsep(&pos, " ");
11998c2ecf20Sopenharmony_ci		if (!token) {
12008c2ecf20Sopenharmony_ci			ret = -EINVAL;
12018c2ecf20Sopenharmony_ci			goto err_free;
12028c2ecf20Sopenharmony_ci		}
12038c2ecf20Sopenharmony_ci
12048c2ecf20Sopenharmony_ci		ret = kstrtou8(token, 16, &val);
12058c2ecf20Sopenharmony_ci		if (ret < 0)
12068c2ecf20Sopenharmony_ci			goto err_free;
12078c2ecf20Sopenharmony_ci
12088c2ecf20Sopenharmony_ci		if (token == buf)
12098c2ecf20Sopenharmony_ci			cmd = val;
12108c2ecf20Sopenharmony_ci		else
12118c2ecf20Sopenharmony_ci			parameters[i++] = val;
12128c2ecf20Sopenharmony_ci
12138c2ecf20Sopenharmony_ci		if (i == 64) {
12148c2ecf20Sopenharmony_ci			ret = -E2BIG;
12158c2ecf20Sopenharmony_ci			goto err_free;
12168c2ecf20Sopenharmony_ci		}
12178c2ecf20Sopenharmony_ci	}
12188c2ecf20Sopenharmony_ci
12198c2ecf20Sopenharmony_ci	ret = mipi_dbi_command_buf(&dbidev->dbi, cmd, parameters, i);
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_cierr_free:
12228c2ecf20Sopenharmony_ci	kfree(buf);
12238c2ecf20Sopenharmony_cierr_exit:
12248c2ecf20Sopenharmony_ci	drm_dev_exit(idx);
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	return ret < 0 ? ret : count;
12278c2ecf20Sopenharmony_ci}
12288c2ecf20Sopenharmony_ci
12298c2ecf20Sopenharmony_cistatic int mipi_dbi_debugfs_command_show(struct seq_file *m, void *unused)
12308c2ecf20Sopenharmony_ci{
12318c2ecf20Sopenharmony_ci	struct mipi_dbi_dev *dbidev = m->private;
12328c2ecf20Sopenharmony_ci	struct mipi_dbi *dbi = &dbidev->dbi;
12338c2ecf20Sopenharmony_ci	u8 cmd, val[4];
12348c2ecf20Sopenharmony_ci	int ret, idx;
12358c2ecf20Sopenharmony_ci	size_t len;
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci	if (!drm_dev_enter(&dbidev->drm, &idx))
12388c2ecf20Sopenharmony_ci		return -ENODEV;
12398c2ecf20Sopenharmony_ci
12408c2ecf20Sopenharmony_ci	for (cmd = 0; cmd < 255; cmd++) {
12418c2ecf20Sopenharmony_ci		if (!mipi_dbi_command_is_read(dbi, cmd))
12428c2ecf20Sopenharmony_ci			continue;
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci		switch (cmd) {
12458c2ecf20Sopenharmony_ci		case MIPI_DCS_READ_MEMORY_START:
12468c2ecf20Sopenharmony_ci		case MIPI_DCS_READ_MEMORY_CONTINUE:
12478c2ecf20Sopenharmony_ci			len = 2;
12488c2ecf20Sopenharmony_ci			break;
12498c2ecf20Sopenharmony_ci		case MIPI_DCS_GET_DISPLAY_ID:
12508c2ecf20Sopenharmony_ci			len = 3;
12518c2ecf20Sopenharmony_ci			break;
12528c2ecf20Sopenharmony_ci		case MIPI_DCS_GET_DISPLAY_STATUS:
12538c2ecf20Sopenharmony_ci			len = 4;
12548c2ecf20Sopenharmony_ci			break;
12558c2ecf20Sopenharmony_ci		default:
12568c2ecf20Sopenharmony_ci			len = 1;
12578c2ecf20Sopenharmony_ci			break;
12588c2ecf20Sopenharmony_ci		}
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci		seq_printf(m, "%02x: ", cmd);
12618c2ecf20Sopenharmony_ci		ret = mipi_dbi_command_buf(dbi, cmd, val, len);
12628c2ecf20Sopenharmony_ci		if (ret) {
12638c2ecf20Sopenharmony_ci			seq_puts(m, "XX\n");
12648c2ecf20Sopenharmony_ci			continue;
12658c2ecf20Sopenharmony_ci		}
12668c2ecf20Sopenharmony_ci		seq_printf(m, "%*phN\n", (int)len, val);
12678c2ecf20Sopenharmony_ci	}
12688c2ecf20Sopenharmony_ci
12698c2ecf20Sopenharmony_ci	drm_dev_exit(idx);
12708c2ecf20Sopenharmony_ci
12718c2ecf20Sopenharmony_ci	return 0;
12728c2ecf20Sopenharmony_ci}
12738c2ecf20Sopenharmony_ci
12748c2ecf20Sopenharmony_cistatic int mipi_dbi_debugfs_command_open(struct inode *inode,
12758c2ecf20Sopenharmony_ci					 struct file *file)
12768c2ecf20Sopenharmony_ci{
12778c2ecf20Sopenharmony_ci	return single_open(file, mipi_dbi_debugfs_command_show,
12788c2ecf20Sopenharmony_ci			   inode->i_private);
12798c2ecf20Sopenharmony_ci}
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_cistatic const struct file_operations mipi_dbi_debugfs_command_fops = {
12828c2ecf20Sopenharmony_ci	.owner = THIS_MODULE,
12838c2ecf20Sopenharmony_ci	.open = mipi_dbi_debugfs_command_open,
12848c2ecf20Sopenharmony_ci	.read = seq_read,
12858c2ecf20Sopenharmony_ci	.llseek = seq_lseek,
12868c2ecf20Sopenharmony_ci	.release = single_release,
12878c2ecf20Sopenharmony_ci	.write = mipi_dbi_debugfs_command_write,
12888c2ecf20Sopenharmony_ci};
12898c2ecf20Sopenharmony_ci
12908c2ecf20Sopenharmony_ci/**
12918c2ecf20Sopenharmony_ci * mipi_dbi_debugfs_init - Create debugfs entries
12928c2ecf20Sopenharmony_ci * @minor: DRM minor
12938c2ecf20Sopenharmony_ci *
12948c2ecf20Sopenharmony_ci * This function creates a 'command' debugfs file for sending commands to the
12958c2ecf20Sopenharmony_ci * controller or getting the read command values.
12968c2ecf20Sopenharmony_ci * Drivers can use this as their &drm_driver->debugfs_init callback.
12978c2ecf20Sopenharmony_ci *
12988c2ecf20Sopenharmony_ci */
12998c2ecf20Sopenharmony_civoid mipi_dbi_debugfs_init(struct drm_minor *minor)
13008c2ecf20Sopenharmony_ci{
13018c2ecf20Sopenharmony_ci	struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(minor->dev);
13028c2ecf20Sopenharmony_ci	umode_t mode = S_IFREG | S_IWUSR;
13038c2ecf20Sopenharmony_ci
13048c2ecf20Sopenharmony_ci	if (dbidev->dbi.read_commands)
13058c2ecf20Sopenharmony_ci		mode |= S_IRUGO;
13068c2ecf20Sopenharmony_ci	debugfs_create_file("command", mode, minor->debugfs_root, dbidev,
13078c2ecf20Sopenharmony_ci			    &mipi_dbi_debugfs_command_fops);
13088c2ecf20Sopenharmony_ci}
13098c2ecf20Sopenharmony_ciEXPORT_SYMBOL(mipi_dbi_debugfs_init);
13108c2ecf20Sopenharmony_ci
13118c2ecf20Sopenharmony_ci#endif
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
1314