669 lines · 19.2 KB · C
RawDownload
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) #include <drm/clients/drm_client_setup.h>#include <drm/drm_atomic.h>#include <drm/drm_atomic_helper.h>#include <drm/drm_connector.h>#include <drm/drm_damage_helper.h>#include <drm/drm_drv.h>#include <drm/drm_fb_dma_helper.h>#include <drm/drm_fbdev_dma.h>#include <drm/drm_format_helper.h>#include <drm/drm_framebuffer.h>#include <drm/drm_gem_atomic_helper.h>#include <drm/drm_gem_dma_helper.h>#include <drm/drm_gem_framebuffer_helper.h>#include <drm/drm_managed.h>#include <drm/drm_modes.h>#include <drm/drm_probe_helper.h>#include <drm/drm_rect.h>#include <linux/bitrev.h>#include <linux/delay.h>#include <linux/gpio/consumer.h>#include <linux/kthread.h>#include <linux/mod_devicetable.h>#include <linux/module.h>#include <linux/mutex.h>#include <linux/pwm.h>#include <linux/spi/spi.h> #define SHARP_MODE_PERIOD 8#define SHARP_ADDR_PERIOD 8#define SHARP_DUMMY_PERIOD 8 #define SHARP_MEMORY_DISPLAY_MAINTAIN_MODE 0#define SHARP_MEMORY_DISPLAY_UPDATE_MODE 1#define SHARP_MEMORY_DISPLAY_CLEAR_MODE 4 enum sharp_memory_model {	LS010B7DH04,	LS011B7DH03,	LS012B7DD01,	LS013B7DH03,	LS013B7DH05,	LS018B7DH02,	LS027B7DH01,	LS027B7DH01A,	LS032B7DD02,	LS044Q7DH01,}; enum sharp_memory_vcom_mode {	SHARP_MEMORY_SOFTWARE_VCOM,	SHARP_MEMORY_EXTERNAL_VCOM,	SHARP_MEMORY_PWM_VCOM}; struct sharp_memory_device {	struct drm_device drm;	struct spi_device *spi; 	const struct drm_display_mode *mode; 	struct drm_crtc crtc;	struct drm_plane plane;	struct drm_encoder encoder;	struct drm_connector connector; 	struct gpio_desc *enable_gpio; 	struct task_struct *sw_vcom_signal;	struct pwm_device *pwm_vcom_signal; 	enum sharp_memory_vcom_mode vcom_mode;	u8 vcom; 	u32 pitch;	u32 tx_buffer_size;	u8 *tx_buffer; 	/* When vcom_mode == "software" a kthread is used to periodically send a	 * 'maintain display' message over spi. This mutex ensures tx_buffer access	 * and spi bus usage is synchronized in this case.	 */	struct mutex tx_mutex;}; static inline int sharp_memory_spi_write(struct spi_device *spi, void *buf, size_t len){	/* Reverse the bit order */	for (u8 *b = buf; b < ((u8 *)buf) + len; ++b)		*b = bitrev8(*b); 	return spi_write(spi, buf, len);} static inline struct sharp_memory_device *drm_to_sharp_memory_device(struct drm_device *drm){	return container_of(drm, struct sharp_memory_device, drm);} DEFINE_DRM_GEM_DMA_FOPS(sharp_memory_fops); static const struct drm_driver sharp_memory_drm_driver = {	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,	.fops			= &sharp_memory_fops,	DRM_GEM_DMA_DRIVER_OPS_VMAP,	DRM_FBDEV_DMA_DRIVER_OPS,	.name			= "sharp_memory_display",	.desc			= "Sharp Display Memory LCD",	.major			= 1,	.minor			= 0,}; static inline void sharp_memory_set_tx_buffer_mode(u8 *buffer, u8 mode, u8 vcom){	*buffer = mode | (vcom << 1);} static inline void sharp_memory_set_tx_buffer_addresses(u8 *buffer,							struct drm_rect clip,							u32 pitch){	for (u32 line = 0; line < clip.y2; ++line)		buffer[line * pitch] = line + 1;} static void sharp_memory_set_tx_buffer_data(u8 *buffer,					    struct drm_framebuffer *fb,					    const struct iosys_map *vmap,					    struct drm_rect clip,					    u32 pitch,					    struct drm_format_conv_state *fmtcnv_state){	int ret;	struct iosys_map dst; 	ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);	if (ret)		return; 	iosys_map_set_vaddr(&dst, buffer); 	drm_fb_xrgb8888_to_mono(&dst, &pitch, vmap, fb, &clip, fmtcnv_state); 	drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);} static int sharp_memory_update_display(struct sharp_memory_device *smd,				       struct drm_framebuffer *fb,				       const struct iosys_map *vmap,				       struct drm_rect clip,				       struct drm_format_conv_state *fmtcnv_state){	int ret;	u32 pitch = smd->pitch;	u8 vcom = smd->vcom;	u8 *tx_buffer = smd->tx_buffer;	u32 tx_buffer_size = smd->tx_buffer_size; 	mutex_lock(&smd->tx_mutex); 	/* Populate the transmit buffer with frame data */	sharp_memory_set_tx_buffer_mode(&tx_buffer[0],					SHARP_MEMORY_DISPLAY_UPDATE_MODE, vcom);	sharp_memory_set_tx_buffer_addresses(&tx_buffer[1], clip, pitch);	sharp_memory_set_tx_buffer_data(&tx_buffer[2], fb, vmap, clip, pitch, fmtcnv_state); 	ret = sharp_memory_spi_write(smd->spi, tx_buffer, tx_buffer_size); 	mutex_unlock(&smd->tx_mutex); 	return ret;} static int sharp_memory_maintain_display(struct sharp_memory_device *smd){	int ret;	u8 vcom = smd->vcom;	u8 *tx_buffer = smd->tx_buffer; 	mutex_lock(&smd->tx_mutex); 	sharp_memory_set_tx_buffer_mode(&tx_buffer[0], SHARP_MEMORY_DISPLAY_MAINTAIN_MODE, vcom);	tx_buffer[1] = 0; /* Write dummy data */	ret = sharp_memory_spi_write(smd->spi, tx_buffer, 2); 	mutex_unlock(&smd->tx_mutex); 	return ret;} static int sharp_memory_clear_display(struct sharp_memory_device *smd){	int ret;	u8 vcom = smd->vcom;	u8 *tx_buffer = smd->tx_buffer; 	mutex_lock(&smd->tx_mutex); 	sharp_memory_set_tx_buffer_mode(&tx_buffer[0], SHARP_MEMORY_DISPLAY_CLEAR_MODE, vcom);	tx_buffer[1] = 0; /* write dummy data */	ret = sharp_memory_spi_write(smd->spi, tx_buffer, 2); 	mutex_unlock(&smd->tx_mutex); 	return ret;} static void sharp_memory_fb_dirty(struct drm_framebuffer *fb, const struct iosys_map *vmap,				  struct drm_rect *rect,				  struct drm_format_conv_state *fmtconv_state){	struct drm_rect clip;	struct sharp_memory_device *smd = drm_to_sharp_memory_device(fb->dev); 	/* Always update a full line regardless of what is dirty */	clip.x1 = 0;	clip.x2 = fb->width;	clip.y1 = rect->y1;	clip.y2 = rect->y2; 	sharp_memory_update_display(smd, fb, vmap, clip, fmtconv_state);} static int sharp_memory_plane_atomic_check(struct drm_plane *plane,					   struct drm_atomic_state *state){	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);	struct sharp_memory_device *smd;	struct drm_crtc_state *crtc_state; 	smd = container_of(plane, struct sharp_memory_device, plane);	crtc_state = drm_atomic_get_new_crtc_state(state, &smd->crtc); 	return drm_atomic_helper_check_plane_state(plane_state, crtc_state,						   DRM_PLANE_NO_SCALING,						   DRM_PLANE_NO_SCALING,						   false, false);} static void sharp_memory_plane_atomic_update(struct drm_plane *plane,					     struct drm_atomic_state *state){	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);	struct drm_plane_state *plane_state = plane->state;	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);	struct sharp_memory_device *smd;	struct drm_rect rect; 	smd = container_of(plane, struct sharp_memory_device, plane);	if (!smd->crtc.state->active)		return; 	if (drm_atomic_helper_damage_merged(old_state, plane_state, &rect))		sharp_memory_fb_dirty(plane_state->fb, shadow_plane_state->data,				      &rect, &shadow_plane_state->fmtcnv_state);} static const struct drm_plane_helper_funcs sharp_memory_plane_helper_funcs = {	.prepare_fb = drm_gem_plane_helper_prepare_fb,	.atomic_check = sharp_memory_plane_atomic_check,	.atomic_update = sharp_memory_plane_atomic_update,	DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,}; static bool sharp_memory_format_mod_supported(struct drm_plane *plane,					      u32 format,					      u64 modifier){	return modifier == DRM_FORMAT_MOD_LINEAR;} static const struct drm_plane_funcs sharp_memory_plane_funcs = {	.update_plane = drm_atomic_helper_update_plane,	.disable_plane = drm_atomic_helper_disable_plane,	.destroy = drm_plane_cleanup,	DRM_GEM_SHADOW_PLANE_FUNCS,	.format_mod_supported = sharp_memory_format_mod_supported,}; static enum drm_mode_status sharp_memory_crtc_mode_valid(struct drm_crtc *crtc,							 const struct drm_display_mode *mode){	struct sharp_memory_device *smd = drm_to_sharp_memory_device(crtc->dev); 	return drm_crtc_helper_mode_valid_fixed(crtc, mode, smd->mode);} static int sharp_memory_crtc_check(struct drm_crtc *crtc,				   struct drm_atomic_state *state){	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);	int ret; 	if (!crtc_state->enable)		goto out; 	ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state);	if (ret)		return ret; out:	return drm_atomic_add_affected_planes(state, crtc);} static int sharp_memory_sw_vcom_signal_thread(void *data){	struct sharp_memory_device *smd = data; 	while (!kthread_should_stop()) {		smd->vcom ^= 1; /* Toggle vcom */		sharp_memory_maintain_display(smd);		msleep(1000);	} 	return 0;} static void sharp_memory_crtc_enable(struct drm_crtc *crtc,				     struct drm_atomic_state *state){	struct sharp_memory_device *smd = drm_to_sharp_memory_device(crtc->dev); 	sharp_memory_clear_display(smd); 	if (smd->enable_gpio)		gpiod_set_value(smd->enable_gpio, 1);} static void sharp_memory_crtc_disable(struct drm_crtc *crtc,				      struct drm_atomic_state *state){	struct sharp_memory_device *smd = drm_to_sharp_memory_device(crtc->dev); 	sharp_memory_clear_display(smd); 	if (smd->enable_gpio)		gpiod_set_value(smd->enable_gpio, 0);} static const struct drm_crtc_helper_funcs sharp_memory_crtc_helper_funcs = {	.mode_valid = sharp_memory_crtc_mode_valid,	.atomic_check = sharp_memory_crtc_check,	.atomic_enable = sharp_memory_crtc_enable,	.atomic_disable = sharp_memory_crtc_disable,}; static const struct drm_crtc_funcs sharp_memory_crtc_funcs = {	.reset = drm_atomic_helper_crtc_reset,	.destroy = drm_crtc_cleanup,	.set_config = drm_atomic_helper_set_config,	.page_flip = drm_atomic_helper_page_flip,	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,}; static const struct drm_encoder_funcs sharp_memory_encoder_funcs = {	.destroy = drm_encoder_cleanup,}; static int sharp_memory_connector_get_modes(struct drm_connector *connector){	struct sharp_memory_device *smd = drm_to_sharp_memory_device(connector->dev); 	return drm_connector_helper_get_modes_fixed(connector, smd->mode);} static const struct drm_connector_helper_funcs sharp_memory_connector_hfuncs = {	.get_modes = sharp_memory_connector_get_modes,}; static const struct drm_connector_funcs sharp_memory_connector_funcs = {	.reset = drm_atomic_helper_connector_reset,	.fill_modes = drm_helper_probe_single_connector_modes,	.destroy = drm_connector_cleanup,	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state, }; static const struct drm_mode_config_funcs sharp_memory_mode_config_funcs = {	.fb_create = drm_gem_fb_create_with_dirty,	.atomic_check = drm_atomic_helper_check,	.atomic_commit = drm_atomic_helper_commit,}; static const struct drm_display_mode sharp_memory_ls010b7dh04_mode = {	DRM_SIMPLE_MODE(128, 128, 18, 18),}; static const struct drm_display_mode sharp_memory_ls011b7dh03_mode = {	DRM_SIMPLE_MODE(160, 68, 25, 10),}; static const struct drm_display_mode sharp_memory_ls012b7dd01_mode = {	DRM_SIMPLE_MODE(184, 38, 29, 6),}; static const struct drm_display_mode sharp_memory_ls013b7dh03_mode = {	DRM_SIMPLE_MODE(128, 128, 23, 23),}; static const struct drm_display_mode sharp_memory_ls013b7dh05_mode = {	DRM_SIMPLE_MODE(144, 168, 20, 24),}; static const struct drm_display_mode sharp_memory_ls018b7dh02_mode = {	DRM_SIMPLE_MODE(230, 303, 27, 36),}; static const struct drm_display_mode sharp_memory_ls027b7dh01_mode = {	DRM_SIMPLE_MODE(400, 240, 58, 35),}; static const struct drm_display_mode sharp_memory_ls032b7dd02_mode = {	DRM_SIMPLE_MODE(336, 536, 42, 68),}; static const struct drm_display_mode sharp_memory_ls044q7dh01_mode = {	DRM_SIMPLE_MODE(320, 240, 89, 67),}; static const struct spi_device_id sharp_memory_ids[] = {	{"ls010b7dh04", (kernel_ulong_t)&sharp_memory_ls010b7dh04_mode},	{"ls011b7dh03", (kernel_ulong_t)&sharp_memory_ls011b7dh03_mode},	{"ls012b7dd01", (kernel_ulong_t)&sharp_memory_ls012b7dd01_mode},	{"ls013b7dh03", (kernel_ulong_t)&sharp_memory_ls013b7dh03_mode},	{"ls013b7dh05", (kernel_ulong_t)&sharp_memory_ls013b7dh05_mode},	{"ls018b7dh02", (kernel_ulong_t)&sharp_memory_ls018b7dh02_mode},	{"ls027b7dh01", (kernel_ulong_t)&sharp_memory_ls027b7dh01_mode},	{"ls027b7dh01a", (kernel_ulong_t)&sharp_memory_ls027b7dh01_mode},	{"ls032b7dd02", (kernel_ulong_t)&sharp_memory_ls032b7dd02_mode},	{"ls044q7dh01", (kernel_ulong_t)&sharp_memory_ls044q7dh01_mode},	{},};MODULE_DEVICE_TABLE(spi, sharp_memory_ids); static const struct of_device_id sharp_memory_of_match[] = {	{.compatible = "sharp,ls010b7dh04", &sharp_memory_ls010b7dh04_mode},	{.compatible = "sharp,ls011b7dh03", &sharp_memory_ls011b7dh03_mode},	{.compatible = "sharp,ls012b7dd01", &sharp_memory_ls012b7dd01_mode},	{.compatible = "sharp,ls013b7dh03", &sharp_memory_ls013b7dh03_mode},	{.compatible = "sharp,ls013b7dh05", &sharp_memory_ls013b7dh05_mode},	{.compatible = "sharp,ls018b7dh02", &sharp_memory_ls018b7dh02_mode},	{.compatible = "sharp,ls027b7dh01", &sharp_memory_ls027b7dh01_mode},	{.compatible = "sharp,ls027b7dh01a", &sharp_memory_ls027b7dh01_mode},	{.compatible = "sharp,ls032b7dd02", &sharp_memory_ls032b7dd02_mode},	{.compatible = "sharp,ls044q7dh01", &sharp_memory_ls044q7dh01_mode},	{},};MODULE_DEVICE_TABLE(of, sharp_memory_of_match); static const u32 sharp_memory_formats[] = {	DRM_FORMAT_XRGB8888,}; static int sharp_memory_pipe_init(struct drm_device *dev,				  struct sharp_memory_device *smd,				  const u32 *formats, unsigned int format_count,				  const u64 *format_modifiers){	int ret;	struct drm_encoder *encoder = &smd->encoder;	struct drm_plane *plane = &smd->plane;	struct drm_crtc *crtc = &smd->crtc;	struct drm_connector *connector = &smd->connector; 	drm_plane_helper_add(plane, &sharp_memory_plane_helper_funcs);	ret = drm_universal_plane_init(dev, plane, 0,				       &sharp_memory_plane_funcs,				       formats, format_count,				       format_modifiers,				       DRM_PLANE_TYPE_PRIMARY, NULL);	if (ret)		return ret; 	drm_crtc_helper_add(crtc, &sharp_memory_crtc_helper_funcs);	ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,					&sharp_memory_crtc_funcs, NULL);	if (ret)		return ret; 	encoder->possible_crtcs = drm_crtc_mask(crtc);	ret = drm_encoder_init(dev, encoder, &sharp_memory_encoder_funcs,			       DRM_MODE_ENCODER_NONE, NULL);	if (ret)		return ret; 	ret = drm_connector_init(&smd->drm, &smd->connector,				 &sharp_memory_connector_funcs,				 DRM_MODE_CONNECTOR_SPI);	if (ret)		return ret; 	drm_connector_helper_add(&smd->connector,				 &sharp_memory_connector_hfuncs); 	return drm_connector_attach_encoder(connector, encoder);} 

Lines 1–500 of 669.