/** * DOC: overview * * This library provides helpers for MIPI Display Bus Interface (DBI) * compatible display controllers. * * Many controllers for tiny lcd displays are MIPI compliant and can use this * library. If a controller uses registers 0x2A and 0x2B to set the area to * update and uses register 0x2C to write to frame memory, it is most likely * MIPI compliant. * * Only MIPI Type 1 displays are supported since a full frame memory is needed. * * There are 3 MIPI DBI implementation types: * * A. Motorola 6800 type parallel bus * * B. Intel 8080 type parallel bus * * C. SPI type with 3 options: * * 1. 9-bit with the Data/Command signal as the ninth bit * 2. Same as above except it's sent as 16 bits * 3. 8-bit with the Data/Command signal as a separate D/CX pin * * Currently mipi_dbi only supports Type C options 1 and 3 with * mipi_dbi_spi_init().
*/
/* This should only be used by mipi_dbi_command() */ int mipi_dbi_command_stackbuf(struct mipi_dbi *dbi, u8 cmd, const u8 *data,
size_t len)
{
u8 *buf; int ret;
buf = kmemdup(data, len, GFP_KERNEL); if (!buf) return -ENOMEM;
if (fb->format->format == DRM_FORMAT_XRGB8888)
dst_format = drm_format_info(dbidev->pixel_format); else
dst_format = fb->format;
len = drm_format_info_min_pitch(dst_format, 0, width) * height;
ret = mipi_dbi_command_buf(dbi, MIPI_DCS_WRITE_MEMORY_START, tr, len);
err_msg: if (ret)
drm_err_once(fb->dev, "Failed to update display %d\n", ret);
}
/** * mipi_dbi_pipe_mode_valid - MIPI DBI mode-valid helper * @pipe: Simple display pipe * @mode: The mode to test * * This function validates a given display mode against the MIPI DBI's hardware * display. Drivers can use this as their &drm_simple_display_pipe_funcs->mode_valid * callback.
*/ enum drm_mode_status mipi_dbi_pipe_mode_valid(struct drm_simple_display_pipe *pipe, conststruct drm_display_mode *mode)
{ struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev);
/** * mipi_dbi_enable_flush - MIPI DBI enable helper * @dbidev: MIPI DBI device structure * @crtc_state: CRTC state * @plane_state: Plane state * * Flushes the whole framebuffer and enables the backlight. Drivers can use this * in their &drm_simple_display_pipe_funcs->enable callback. * * Note: Drivers which don't use mipi_dbi_pipe_update() because they have custom * framebuffer flushing, can't use this function since they both use the same * flushing code.
*/ void mipi_dbi_enable_flush(struct mipi_dbi_dev *dbidev, struct drm_crtc_state *crtc_state, struct drm_plane_state *plane_state)
{ struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state); struct drm_framebuffer *fb = plane_state->fb; struct drm_rect rect = {
.x1 = 0,
.x2 = fb->width,
.y1 = 0,
.y2 = fb->height,
}; int idx;
/** * mipi_dbi_pipe_disable - MIPI DBI pipe disable helper * @pipe: Display pipe * * This function disables backlight if present, if not the display memory is * blanked. The regulator is disabled if in use. Drivers can use this as their * &drm_simple_display_pipe_funcs->disable callback.
*/ void mipi_dbi_pipe_disable(struct drm_simple_display_pipe *pipe)
{ struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(pipe->crtc.dev);
DRM_DEBUG_KMS("\n");
if (dbidev->backlight)
backlight_disable(dbidev->backlight); else
mipi_dbi_blank(dbidev);
if (dbidev->regulator)
regulator_disable(dbidev->regulator); if (dbidev->io_regulator)
regulator_disable(dbidev->io_regulator);
}
EXPORT_SYMBOL(mipi_dbi_pipe_disable);
/** * mipi_dbi_pipe_begin_fb_access - MIPI DBI pipe begin-access helper * @pipe: Display pipe * @plane_state: Plane state * * This function implements struct &drm_simple_display_funcs.begin_fb_access. * * See drm_gem_begin_shadow_fb_access() for details and mipi_dbi_pipe_cleanup_fb() * for cleanup. * * Returns: * 0 on success, or a negative errno code otherwise.
*/ int mipi_dbi_pipe_begin_fb_access(struct drm_simple_display_pipe *pipe, struct drm_plane_state *plane_state)
{ return drm_gem_begin_shadow_fb_access(&pipe->plane, plane_state);
}
EXPORT_SYMBOL(mipi_dbi_pipe_begin_fb_access);
/** * mipi_dbi_pipe_duplicate_plane_state - duplicates MIPI DBI plane state * @pipe: Display pipe * * This function implements struct &drm_simple_display_funcs.duplicate_plane_state * for MIPI DBI planes. * * See drm_gem_duplicate_shadow_plane_state() for additional details. * * Returns: * A pointer to a new plane state on success, or NULL otherwise.
*/ struct drm_plane_state *mipi_dbi_pipe_duplicate_plane_state(struct drm_simple_display_pipe *pipe)
{ return drm_gem_duplicate_shadow_plane_state(&pipe->plane);
}
EXPORT_SYMBOL(mipi_dbi_pipe_duplicate_plane_state);
/** * mipi_dbi_pipe_destroy_plane_state - cleans up MIPI DBI plane state * @pipe: Display pipe * @plane_state: Plane state * * This function implements struct drm_simple_display_funcs.destroy_plane_state * for MIPI DBI planes. * * See drm_gem_destroy_shadow_plane_state() for additional details.
*/ void mipi_dbi_pipe_destroy_plane_state(struct drm_simple_display_pipe *pipe, struct drm_plane_state *plane_state)
{
drm_gem_destroy_shadow_plane_state(&pipe->plane, plane_state);
}
EXPORT_SYMBOL(mipi_dbi_pipe_destroy_plane_state);
/** * mipi_dbi_dev_init_with_formats - MIPI DBI device initialization with custom formats * @dbidev: MIPI DBI device structure to initialize * @funcs: Display pipe functions * @formats: Array of supported formats (DRM_FORMAT\_\*). * @format_count: Number of elements in @formats * @mode: Display mode * @rotation: Initial rotation in degrees Counter Clock Wise * @tx_buf_size: Allocate a transmit buffer of this size. * * This function sets up a &drm_simple_display_pipe with a &drm_connector that * has one fixed &drm_display_mode which is rotated according to @rotation. * This mode is used to set the mode config min/max width/height properties. * * Use mipi_dbi_dev_init() if you want native RGB565 and emulated XRGB8888 format. * * Note: * Some of the helper functions expects RGB565 to be the default format and the * transmit buffer sized to fit that. * * Returns: * Zero on success, negative error code on failure.
*/ int mipi_dbi_dev_init_with_formats(struct mipi_dbi_dev *dbidev, conststruct drm_simple_display_pipe_funcs *funcs, const uint32_t *formats, unsignedint format_count, conststruct drm_display_mode *mode, unsignedint rotation, size_t tx_buf_size)
{ staticconst uint64_t modifiers[] = {
DRM_FORMAT_MOD_LINEAR,
DRM_FORMAT_MOD_INVALID
}; struct drm_device *drm = &dbidev->drm; int ret;
if (!dbidev->dbi.command) return -EINVAL;
ret = drmm_mode_config_init(drm); if (ret) return ret;
dbidev->tx_buf = devm_kmalloc(drm->dev, tx_buf_size, GFP_KERNEL); if (!dbidev->tx_buf) return -ENOMEM;
drm_mode_copy(&dbidev->mode, mode);
ret = mipi_dbi_rotate_mode(&dbidev->mode, rotation); if (ret) {
DRM_ERROR("Illegal rotation value %u\n", rotation); return -EINVAL;
}
drm_connector_helper_add(&dbidev->connector, &mipi_dbi_connector_hfuncs);
ret = drm_connector_init(drm, &dbidev->connector, &mipi_dbi_connector_funcs,
DRM_MODE_CONNECTOR_SPI); if (ret) return ret;
ret = drm_simple_display_pipe_init(drm, &dbidev->pipe, funcs, formats, format_count,
modifiers, &dbidev->connector); if (ret) return ret;
/** * mipi_dbi_dev_init - MIPI DBI device initialization * @dbidev: MIPI DBI device structure to initialize * @funcs: Display pipe functions * @mode: Display mode * @rotation: Initial rotation in degrees Counter Clock Wise * * This function sets up a &drm_simple_display_pipe with a &drm_connector that * has one fixed &drm_display_mode which is rotated according to @rotation. * This mode is used to set the mode config min/max width/height properties. * Additionally &mipi_dbi.tx_buf is allocated. * * Supported formats: Native RGB565 and emulated XRGB8888. * * Returns: * Zero on success, negative error code on failure.
*/ int mipi_dbi_dev_init(struct mipi_dbi_dev *dbidev, conststruct drm_simple_display_pipe_funcs *funcs, conststruct drm_display_mode *mode, unsignedint rotation)
{
size_t bufsize = mode->vdisplay * mode->hdisplay * sizeof(u16);
/** * mipi_dbi_display_is_on - Check if display is on * @dbi: MIPI DBI structure * * This function checks the Power Mode register (if readable) to see if * display output is turned on. This can be used to see if the bootloader * has already turned on the display avoiding flicker when the pipeline is * enabled. * * Returns: * true if the display can be verified to be on, false otherwise.
*/ bool mipi_dbi_display_is_on(struct mipi_dbi *dbi)
{
u8 val;
if (mipi_dbi_command_read(dbi, MIPI_DCS_GET_POWER_MODE, &val)) returnfalse;
val &= ~DCS_POWER_MODE_RESERVED_MASK;
/* The poweron/reset value is 08h DCS_POWER_MODE_DISPLAY_NORMAL_MODE */ if (val != (DCS_POWER_MODE_DISPLAY |
DCS_POWER_MODE_DISPLAY_NORMAL_MODE | DCS_POWER_MODE_SLEEP_MODE)) returnfalse;
if (dbidev->regulator) {
ret = regulator_enable(dbidev->regulator); if (ret) {
DRM_DEV_ERROR(dev, "Failed to enable regulator (%d)\n", ret); return ret;
}
}
if (dbidev->io_regulator) {
ret = regulator_enable(dbidev->io_regulator); if (ret) {
DRM_DEV_ERROR(dev, "Failed to enable I/O regulator (%d)\n", ret); if (dbidev->regulator)
regulator_disable(dbidev->regulator); return ret;
}
}
if (cond && mipi_dbi_display_is_on(dbi)) return 1;
mipi_dbi_hw_reset(dbi);
ret = mipi_dbi_command(dbi, MIPI_DCS_SOFT_RESET); if (ret) {
DRM_DEV_ERROR(dev, "Failed to send reset command (%d)\n", ret); if (dbidev->regulator)
regulator_disable(dbidev->regulator); if (dbidev->io_regulator)
regulator_disable(dbidev->io_regulator); return ret;
}
/* * If we did a hw reset, we know the controller is in Sleep mode and * per MIPI DSC spec should wait 5ms after soft reset. If we didn't, * we assume worst case and wait 120ms.
*/ if (dbi->reset)
usleep_range(5000, 20000); else
msleep(120);
return 0;
}
/** * mipi_dbi_poweron_reset - MIPI DBI poweron and reset * @dbidev: MIPI DBI device structure * * This function enables the regulator if used and does a hardware and software * reset. * * Returns: * Zero on success, or a negative error code.
*/ int mipi_dbi_poweron_reset(struct mipi_dbi_dev *dbidev)
{ return mipi_dbi_poweron_reset_conditional(dbidev, false);
}
EXPORT_SYMBOL(mipi_dbi_poweron_reset);
/** * mipi_dbi_poweron_conditional_reset - MIPI DBI poweron and conditional reset * @dbidev: MIPI DBI device structure * * This function enables the regulator if used and if the display is off, it * does a hardware and software reset. If mipi_dbi_display_is_on() determines * that the display is on, no reset is performed. * * Returns: * Zero if the controller was reset, 1 if the display was already on, or a * negative error code.
*/ int mipi_dbi_poweron_conditional_reset(struct mipi_dbi_dev *dbidev)
{ return mipi_dbi_poweron_reset_conditional(dbidev, true);
}
EXPORT_SYMBOL(mipi_dbi_poweron_conditional_reset);
#if IS_ENABLED(CONFIG_SPI)
/** * mipi_dbi_spi_cmd_max_speed - get the maximum SPI bus speed * @spi: SPI device * @len: The transfer buffer length. * * Many controllers have a max speed of 10MHz, but can be pushed way beyond * that. Increase reliability by running pixel data at max speed and the rest * at 10MHz, preventing transfer glitches from messing up the init settings.
*/
u32 mipi_dbi_spi_cmd_max_speed(struct spi_device *spi, size_t len)
{ if (len > 64) return 0; /* use default */
/* * MIPI DBI Type C Option 1 * * If the SPI controller doesn't have 9 bits per word support, * use blocks of 9 bytes to send 8x 9-bit words using a 8-bit SPI transfer. * Pad partial blocks with MIPI_DCS_NOP (zero). * This is how the D/C bit (x) is added: * x7654321 * 0x765432 * 10x76543 * 210x7654 * 3210x765 * 43210x76 * 543210x7 * 6543210x * 76543210
*/
if (!dc) { if (WARN_ON_ONCE(len != 1)) return -EINVAL;
/* Command: pad no-op's (zeroes) at beginning of block */
dst = dbi->tx_buf9;
memset(dst, 0, 9);
dst[8] = *src;
tr.len = 9;
return spi_sync(spi, &m);
}
/* max with room for adding one bit per byte */
max_chunk = max_chunk / 9 * 8; /* but no bigger than len */
max_chunk = min(max_chunk, len); /* 8 byte blocks */
max_chunk = max_t(size_t, 8, max_chunk & ~0x7);
while (len) {
size_t added = 0;
chunk = min(len, max_chunk);
len -= chunk;
dst = dbi->tx_buf9;
if (chunk < 8) {
u8 val, carry = 0;
/* Data: pad no-op's (zeroes) at end of block */
memset(dst, 0, 9);
if (swap_bytes) { for (i = 1; i < (chunk + 1); i++) {
val = src[1];
*dst++ = carry | BIT(8 - i) | (val >> i);
carry = val << (8 - i);
i++;
val = src[0];
*dst++ = carry | BIT(8 - i) | (val >> i);
carry = val << (8 - i);
src += 2;
}
*dst++ = carry;
} else { for (i = 1; i < (chunk + 1); i++) {
val = *src++;
*dst++ = carry | BIT(8 - i) | (val >> i);
carry = val << (8 - i);
}
*dst++ = carry;
}
if (!spi_is_bpw_supported(spi, 9)) { /* * FIXME: implement something like mipi_dbi_spi1e_transfer() but * for reads using emulation.
*/
dev_err(&spi->dev, "reading on host not supporting 9 bpw not yet implemented\n"); return -EOPNOTSUPP;
}
/* * Turn the 8bit command into a 16bit version of the command in the * buffer. Only 9 bits of this will be used when executing the actual * transfer.
*/
dst16 = dbi->tx_buf9;
dst16[0] = *cmd;
spi_message_init_with_transfers(&m, tr, ARRAY_SIZE(tr));
ret = spi_sync(spi, &m);
if (!ret)
MIPI_DBI_DEBUG_COMMAND(*cmd, data, len);
/* * Support non-standard 24-bit and 32-bit Nokia read commands which * start with a dummy clock, so we need to read an extra byte.
*/ if (*cmd == MIPI_DCS_GET_DISPLAY_ID ||
*cmd == MIPI_DCS_GET_DISPLAY_STATUS) { if (!(len == 3 || len == 4)) return -EINVAL;
tr[1].len = len + 1;
}
buf = kmalloc(tr[1].len, GFP_KERNEL); if (!buf) return -ENOMEM;
/** * mipi_dbi_spi_init - Initialize MIPI DBI SPI interface * @spi: SPI device * @dbi: MIPI DBI structure to initialize * @dc: D/C gpio (optional) * * This function sets &mipi_dbi->command, enables &mipi_dbi->read_commands for the * usual read commands. It should be followed by a call to mipi_dbi_dev_init() or * a driver-specific init. * * If @dc is set, a Type C Option 3 interface is assumed, if not * Type C Option 1. * * If the command is %MIPI_DCS_WRITE_MEMORY_START and the pixel format is RGB565, endianness has * to be taken into account. The MIPI DBI serial interface is big endian and framebuffers are * assumed stored in memory as little endian (%DRM_FORMAT_BIG_ENDIAN is not supported). * * This is how endianness is handled: * * Option 1 (D/C as a bit): The buffer is sent on the wire byte by byte so the 16-bit buffer is * byteswapped before transfer. * * Option 3 (D/C as a gpio): If the SPI controller supports 16 bits per word the buffer can be * sent as-is. If not the caller is responsible for swapping the bytes * before calling mipi_dbi_command_buf() and the buffer is sent 8 bpw. * * This handling is optimised for %DRM_FORMAT_RGB565 framebuffers. * * If the interface is Option 1 and the SPI controller doesn't support 9 bits per word, * the buffer is sent as 9x 8-bit words, padded with MIPI DCS no-op commands if necessary. * * Returns: * Zero on success, negative error code on failure.
*/ int mipi_dbi_spi_init(struct spi_device *spi, struct mipi_dbi *dbi, struct gpio_desc *dc)
{ struct device *dev = &spi->dev; int ret;
/* * Even though it's not the SPI device that does DMA (the master does), * the dma mask is necessary for the dma_alloc_wc() in the GEM code * (e.g., drm_gem_dma_create()). The dma_addr returned will be a physical * address which might be different from the bus address, but this is * not a problem since the address will not be used. * The virtual address is used in the transfer and the SPI core * re-maps it on the SPI master device using the DMA streaming API * (spi_map_buf()).
*/ if (!dev->coherent_dma_mask) {
ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32)); if (ret) {
dev_warn(dev, "Failed to set dma mask %d\n", ret); return ret;
}
}
/** * mipi_dbi_spi_transfer - SPI transfer helper * @spi: SPI device * @speed_hz: Override speed (optional) * @bpw: Bits per word * @buf: Buffer to transfer * @len: Buffer length * * This SPI transfer helper breaks up the transfer of @buf into chunks which * the SPI controller driver can handle. The SPI bus must be locked when * calling this. * * Returns: * Zero on success, negative error code on failure.
*/ int mipi_dbi_spi_transfer(struct spi_device *spi, u32 speed_hz,
u8 bpw, constvoid *buf, size_t len)
{
size_t max_chunk = spi_max_transfer_size(spi); struct spi_transfer tr = {
.bits_per_word = bpw,
.speed_hz = speed_hz,
}; struct spi_message m;
size_t chunk; int ret;
/* In __spi_validate, there's a validation that no partial transfers * are accepted (xfer->len % w_size must be zero). * Here we align max_chunk to multiple of 2 (16bits), * to prevent transfers from being rejected.
*/
max_chunk = ALIGN_DOWN(max_chunk, 2);
if (!drm_dev_enter(&dbidev->drm, &idx)) return -ENODEV;
for (cmd = 0; cmd < 255; cmd++) { if (!mipi_dbi_command_is_read(dbi, cmd)) continue;
switch (cmd) { case MIPI_DCS_READ_MEMORY_START: case MIPI_DCS_READ_MEMORY_CONTINUE:
len = 2; break; case MIPI_DCS_GET_DISPLAY_ID:
len = 3; break; case MIPI_DCS_GET_DISPLAY_STATUS:
len = 4; break; default:
len = 1; break;
}
/** * mipi_dbi_debugfs_init - Create debugfs entries * @minor: DRM minor * * This function creates a 'command' debugfs file for sending commands to the * controller or getting the read command values. * Drivers can use this as their &drm_driver->debugfs_init callback. *
*/ void mipi_dbi_debugfs_init(struct drm_minor *minor)
{ struct mipi_dbi_dev *dbidev = drm_to_mipi_dbi_dev(minor->dev);
umode_t mode = S_IFREG | S_IWUSR;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.