File Index
Files and directories
Sign in
FILE

sharp-memory.c

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/drm/tiny/sharp-memory.c C
  1// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
  2
  3#include <drm/clients/drm_client_setup.h>
  4#include <drm/drm_atomic.h>
  5#include <drm/drm_atomic_helper.h>
  6#include <drm/drm_connector.h>
  7#include <drm/drm_damage_helper.h>
  8#include <drm/drm_drv.h>
  9#include <drm/drm_fb_dma_helper.h>
 10#include <drm/drm_fbdev_dma.h>
 11#include <drm/drm_format_helper.h>
 12#include <drm/drm_framebuffer.h>
 13#include <drm/drm_gem_atomic_helper.h>
 14#include <drm/drm_gem_dma_helper.h>
 15#include <drm/drm_gem_framebuffer_helper.h>
 16#include <drm/drm_managed.h>
 17#include <drm/drm_modes.h>
 18#include <drm/drm_probe_helper.h>
 19#include <drm/drm_rect.h>
 20#include <linux/bitrev.h>
 21#include <linux/delay.h>
 22#include <linux/gpio/consumer.h>
 23#include <linux/kthread.h>
 24#include <linux/mod_devicetable.h>
 25#include <linux/module.h>
 26#include <linux/mutex.h>
 27#include <linux/pwm.h>
 28#include <linux/spi/spi.h>
 29
 30#define SHARP_MODE_PERIOD 8
 31#define SHARP_ADDR_PERIOD 8
 32#define SHARP_DUMMY_PERIOD 8
 33
 34#define SHARP_MEMORY_DISPLAY_MAINTAIN_MODE 0
 35#define SHARP_MEMORY_DISPLAY_UPDATE_MODE 1
 36#define SHARP_MEMORY_DISPLAY_CLEAR_MODE 4
 37
 38enum sharp_memory_model {
 39	LS010B7DH04,
 40	LS011B7DH03,
 41	LS012B7DD01,
 42	LS013B7DH03,
 43	LS013B7DH05,
 44	LS018B7DH02,
 45	LS027B7DH01,
 46	LS027B7DH01A,
 47	LS032B7DD02,
 48	LS044Q7DH01,
 49};
 50
 51enum sharp_memory_vcom_mode {
 52	SHARP_MEMORY_SOFTWARE_VCOM,
 53	SHARP_MEMORY_EXTERNAL_VCOM,
 54	SHARP_MEMORY_PWM_VCOM
 55};
 56
 57struct sharp_memory_device {
 58	struct drm_device drm;
 59	struct spi_device *spi;
 60
 61	const struct drm_display_mode *mode;
 62
 63	struct drm_crtc crtc;
 64	struct drm_plane plane;
 65	struct drm_encoder encoder;
 66	struct drm_connector connector;
 67
 68	struct gpio_desc *enable_gpio;
 69
 70	struct task_struct *sw_vcom_signal;
 71	struct pwm_device *pwm_vcom_signal;
 72
 73	enum sharp_memory_vcom_mode vcom_mode;
 74	u8 vcom;
 75
 76	u32 pitch;
 77	u32 tx_buffer_size;
 78	u8 *tx_buffer;
 79
 80	/* When vcom_mode == "software" a kthread is used to periodically send a
 81	 * 'maintain display' message over spi. This mutex ensures tx_buffer access
 82	 * and spi bus usage is synchronized in this case.
 83	 */
 84	struct mutex tx_mutex;
 85};
 86
 87static inline int sharp_memory_spi_write(struct spi_device *spi, void *buf, size_t len)
 88{
 89	/* Reverse the bit order */
 90	for (u8 *b = buf; b < ((u8 *)buf) + len; ++b)
 91		*b = bitrev8(*b);
 92
 93	return spi_write(spi, buf, len);
 94}
 95
 96static inline struct sharp_memory_device *drm_to_sharp_memory_device(struct drm_device *drm)
 97{
 98	return container_of(drm, struct sharp_memory_device, drm);
 99}
100
101DEFINE_DRM_GEM_DMA_FOPS(sharp_memory_fops);
102
103static const struct drm_driver sharp_memory_drm_driver = {
104	.driver_features	= DRIVER_GEM | DRIVER_MODESET | DRIVER_ATOMIC,
105	.fops			= &sharp_memory_fops,
106	DRM_GEM_DMA_DRIVER_OPS_VMAP,
107	DRM_FBDEV_DMA_DRIVER_OPS,
108	.name			= "sharp_memory_display",
109	.desc			= "Sharp Display Memory LCD",
110	.major			= 1,
111	.minor			= 0,
112};
113
114static inline void sharp_memory_set_tx_buffer_mode(u8 *buffer, u8 mode, u8 vcom)
115{
116	*buffer = mode | (vcom << 1);
117}
118
119static inline void sharp_memory_set_tx_buffer_addresses(u8 *buffer,
120							struct drm_rect clip,
121							u32 pitch)
122{
123	for (u32 line = 0; line < clip.y2; ++line)
124		buffer[line * pitch] = line + 1;
125}
126
127static void sharp_memory_set_tx_buffer_data(u8 *buffer,
128					    struct drm_framebuffer *fb,
129					    const struct iosys_map *vmap,
130					    struct drm_rect clip,
131					    u32 pitch,
132					    struct drm_format_conv_state *fmtcnv_state)
133{
134	int ret;
135	struct iosys_map dst;
136
137	ret = drm_gem_fb_begin_cpu_access(fb, DMA_FROM_DEVICE);
138	if (ret)
139		return;
140
141	iosys_map_set_vaddr(&dst, buffer);
142
143	drm_fb_xrgb8888_to_mono(&dst, &pitch, vmap, fb, &clip, fmtcnv_state);
144
145	drm_gem_fb_end_cpu_access(fb, DMA_FROM_DEVICE);
146}
147
148static int sharp_memory_update_display(struct sharp_memory_device *smd,
149				       struct drm_framebuffer *fb,
150				       const struct iosys_map *vmap,
151				       struct drm_rect clip,
152				       struct drm_format_conv_state *fmtcnv_state)
153{
154	int ret;
155	u32 pitch = smd->pitch;
156	u8 vcom = smd->vcom;
157	u8 *tx_buffer = smd->tx_buffer;
158	u32 tx_buffer_size = smd->tx_buffer_size;
159
160	mutex_lock(&smd->tx_mutex);
161
162	/* Populate the transmit buffer with frame data */
163	sharp_memory_set_tx_buffer_mode(&tx_buffer[0],
164					SHARP_MEMORY_DISPLAY_UPDATE_MODE, vcom);
165	sharp_memory_set_tx_buffer_addresses(&tx_buffer[1], clip, pitch);
166	sharp_memory_set_tx_buffer_data(&tx_buffer[2], fb, vmap, clip, pitch, fmtcnv_state);
167
168	ret = sharp_memory_spi_write(smd->spi, tx_buffer, tx_buffer_size);
169
170	mutex_unlock(&smd->tx_mutex);
171
172	return ret;
173}
174
175static int sharp_memory_maintain_display(struct sharp_memory_device *smd)
176{
177	int ret;
178	u8 vcom = smd->vcom;
179	u8 *tx_buffer = smd->tx_buffer;
180
181	mutex_lock(&smd->tx_mutex);
182
183	sharp_memory_set_tx_buffer_mode(&tx_buffer[0], SHARP_MEMORY_DISPLAY_MAINTAIN_MODE, vcom);
184	tx_buffer[1] = 0; /* Write dummy data */
185	ret = sharp_memory_spi_write(smd->spi, tx_buffer, 2);
186
187	mutex_unlock(&smd->tx_mutex);
188
189	return ret;
190}
191
192static int sharp_memory_clear_display(struct sharp_memory_device *smd)
193{
194	int ret;
195	u8 vcom = smd->vcom;
196	u8 *tx_buffer = smd->tx_buffer;
197
198	mutex_lock(&smd->tx_mutex);
199
200	sharp_memory_set_tx_buffer_mode(&tx_buffer[0], SHARP_MEMORY_DISPLAY_CLEAR_MODE, vcom);
201	tx_buffer[1] = 0; /* write dummy data */
202	ret = sharp_memory_spi_write(smd->spi, tx_buffer, 2);
203
204	mutex_unlock(&smd->tx_mutex);
205
206	return ret;
207}
208
209static void sharp_memory_fb_dirty(struct drm_framebuffer *fb, const struct iosys_map *vmap,
210				  struct drm_rect *rect,
211				  struct drm_format_conv_state *fmtconv_state)
212{
213	struct drm_rect clip;
214	struct sharp_memory_device *smd = drm_to_sharp_memory_device(fb->dev);
215
216	/* Always update a full line regardless of what is dirty */
217	clip.x1 = 0;
218	clip.x2 = fb->width;
219	clip.y1 = rect->y1;
220	clip.y2 = rect->y2;
221
222	sharp_memory_update_display(smd, fb, vmap, clip, fmtconv_state);
223}
224
225static int sharp_memory_plane_atomic_check(struct drm_plane *plane,
226					   struct drm_atomic_state *state)
227{
228	struct drm_plane_state *plane_state = drm_atomic_get_new_plane_state(state, plane);
229	struct sharp_memory_device *smd;
230	struct drm_crtc_state *crtc_state;
231
232	smd = container_of(plane, struct sharp_memory_device, plane);
233	crtc_state = drm_atomic_get_new_crtc_state(state, &smd->crtc);
234
235	return drm_atomic_helper_check_plane_state(plane_state, crtc_state,
236						   DRM_PLANE_NO_SCALING,
237						   DRM_PLANE_NO_SCALING,
238						   false, false);
239}
240
241static void sharp_memory_plane_atomic_update(struct drm_plane *plane,
242					     struct drm_atomic_state *state)
243{
244	struct drm_plane_state *old_state = drm_atomic_get_old_plane_state(state, plane);
245	struct drm_plane_state *plane_state = plane->state;
246	struct drm_shadow_plane_state *shadow_plane_state = to_drm_shadow_plane_state(plane_state);
247	struct sharp_memory_device *smd;
248	struct drm_rect rect;
249
250	smd = container_of(plane, struct sharp_memory_device, plane);
251	if (!smd->crtc.state->active)
252		return;
253
254	if (drm_atomic_helper_damage_merged(old_state, plane_state, &rect))
255		sharp_memory_fb_dirty(plane_state->fb, shadow_plane_state->data,
256				      &rect, &shadow_plane_state->fmtcnv_state);
257}
258
259static const struct drm_plane_helper_funcs sharp_memory_plane_helper_funcs = {
260	.prepare_fb = drm_gem_plane_helper_prepare_fb,
261	.atomic_check = sharp_memory_plane_atomic_check,
262	.atomic_update = sharp_memory_plane_atomic_update,
263	DRM_GEM_SHADOW_PLANE_HELPER_FUNCS,
264};
265
266static bool sharp_memory_format_mod_supported(struct drm_plane *plane,
267					      u32 format,
268					      u64 modifier)
269{
270	return modifier == DRM_FORMAT_MOD_LINEAR;
271}
272
273static const struct drm_plane_funcs sharp_memory_plane_funcs = {
274	.update_plane = drm_atomic_helper_update_plane,
275	.disable_plane = drm_atomic_helper_disable_plane,
276	.destroy = drm_plane_cleanup,
277	DRM_GEM_SHADOW_PLANE_FUNCS,
278	.format_mod_supported = sharp_memory_format_mod_supported,
279};
280
281static enum drm_mode_status sharp_memory_crtc_mode_valid(struct drm_crtc *crtc,
282							 const struct drm_display_mode *mode)
283{
284	struct sharp_memory_device *smd = drm_to_sharp_memory_device(crtc->dev);
285
286	return drm_crtc_helper_mode_valid_fixed(crtc, mode, smd->mode);
287}
288
289static int sharp_memory_crtc_check(struct drm_crtc *crtc,
290				   struct drm_atomic_state *state)
291{
292	struct drm_crtc_state *crtc_state = drm_atomic_get_new_crtc_state(state, crtc);
293	int ret;
294
295	if (!crtc_state->enable)
296		goto out;
297
298	ret = drm_atomic_helper_check_crtc_primary_plane(crtc_state);
299	if (ret)
300		return ret;
301
302out:
303	return drm_atomic_add_affected_planes(state, crtc);
304}
305
306static int sharp_memory_sw_vcom_signal_thread(void *data)
307{
308	struct sharp_memory_device *smd = data;
309
310	while (!kthread_should_stop()) {
311		smd->vcom ^= 1; /* Toggle vcom */
312		sharp_memory_maintain_display(smd);
313		msleep(1000);
314	}
315
316	return 0;
317}
318
319static void sharp_memory_crtc_enable(struct drm_crtc *crtc,
320				     struct drm_atomic_state *state)
321{
322	struct sharp_memory_device *smd = drm_to_sharp_memory_device(crtc->dev);
323
324	sharp_memory_clear_display(smd);
325
326	if (smd->enable_gpio)
327		gpiod_set_value(smd->enable_gpio, 1);
328}
329
330static void sharp_memory_crtc_disable(struct drm_crtc *crtc,
331				      struct drm_atomic_state *state)
332{
333	struct sharp_memory_device *smd = drm_to_sharp_memory_device(crtc->dev);
334
335	sharp_memory_clear_display(smd);
336
337	if (smd->enable_gpio)
338		gpiod_set_value(smd->enable_gpio, 0);
339}
340
341static const struct drm_crtc_helper_funcs sharp_memory_crtc_helper_funcs = {
342	.mode_valid = sharp_memory_crtc_mode_valid,
343	.atomic_check = sharp_memory_crtc_check,
344	.atomic_enable = sharp_memory_crtc_enable,
345	.atomic_disable = sharp_memory_crtc_disable,
346};
347
348static const struct drm_crtc_funcs sharp_memory_crtc_funcs = {
349	.reset = drm_atomic_helper_crtc_reset,
350	.destroy = drm_crtc_cleanup,
351	.set_config = drm_atomic_helper_set_config,
352	.page_flip = drm_atomic_helper_page_flip,
353	.atomic_duplicate_state = drm_atomic_helper_crtc_duplicate_state,
354	.atomic_destroy_state = drm_atomic_helper_crtc_destroy_state,
355};
356
357static const struct drm_encoder_funcs sharp_memory_encoder_funcs = {
358	.destroy = drm_encoder_cleanup,
359};
360
361static int sharp_memory_connector_get_modes(struct drm_connector *connector)
362{
363	struct sharp_memory_device *smd = drm_to_sharp_memory_device(connector->dev);
364
365	return drm_connector_helper_get_modes_fixed(connector, smd->mode);
366}
367
368static const struct drm_connector_helper_funcs sharp_memory_connector_hfuncs = {
369	.get_modes = sharp_memory_connector_get_modes,
370};
371
372static const struct drm_connector_funcs sharp_memory_connector_funcs = {
373	.reset = drm_atomic_helper_connector_reset,
374	.fill_modes = drm_helper_probe_single_connector_modes,
375	.destroy = drm_connector_cleanup,
376	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
377	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
378
379};
380
381static const struct drm_mode_config_funcs sharp_memory_mode_config_funcs = {
382	.fb_create = drm_gem_fb_create_with_dirty,
383	.atomic_check = drm_atomic_helper_check,
384	.atomic_commit = drm_atomic_helper_commit,
385};
386
387static const struct drm_display_mode sharp_memory_ls010b7dh04_mode = {
388	DRM_SIMPLE_MODE(128, 128, 18, 18),
389};
390
391static const struct drm_display_mode sharp_memory_ls011b7dh03_mode = {
392	DRM_SIMPLE_MODE(160, 68, 25, 10),
393};
394
395static const struct drm_display_mode sharp_memory_ls012b7dd01_mode = {
396	DRM_SIMPLE_MODE(184, 38, 29, 6),
397};
398
399static const struct drm_display_mode sharp_memory_ls013b7dh03_mode = {
400	DRM_SIMPLE_MODE(128, 128, 23, 23),
401};
402
403static const struct drm_display_mode sharp_memory_ls013b7dh05_mode = {
404	DRM_SIMPLE_MODE(144, 168, 20, 24),
405};
406
407static const struct drm_display_mode sharp_memory_ls018b7dh02_mode = {
408	DRM_SIMPLE_MODE(230, 303, 27, 36),
409};
410
411static const struct drm_display_mode sharp_memory_ls027b7dh01_mode = {
412	DRM_SIMPLE_MODE(400, 240, 58, 35),
413};
414
415static const struct drm_display_mode sharp_memory_ls032b7dd02_mode = {
416	DRM_SIMPLE_MODE(336, 536, 42, 68),
417};
418
419static const struct drm_display_mode sharp_memory_ls044q7dh01_mode = {
420	DRM_SIMPLE_MODE(320, 240, 89, 67),
421};
422
423static const struct spi_device_id sharp_memory_ids[] = {
424	{"ls010b7dh04", (kernel_ulong_t)&sharp_memory_ls010b7dh04_mode},
425	{"ls011b7dh03", (kernel_ulong_t)&sharp_memory_ls011b7dh03_mode},
426	{"ls012b7dd01", (kernel_ulong_t)&sharp_memory_ls012b7dd01_mode},
427	{"ls013b7dh03", (kernel_ulong_t)&sharp_memory_ls013b7dh03_mode},
428	{"ls013b7dh05", (kernel_ulong_t)&sharp_memory_ls013b7dh05_mode},
429	{"ls018b7dh02", (kernel_ulong_t)&sharp_memory_ls018b7dh02_mode},
430	{"ls027b7dh01", (kernel_ulong_t)&sharp_memory_ls027b7dh01_mode},
431	{"ls027b7dh01a", (kernel_ulong_t)&sharp_memory_ls027b7dh01_mode},
432	{"ls032b7dd02", (kernel_ulong_t)&sharp_memory_ls032b7dd02_mode},
433	{"ls044q7dh01", (kernel_ulong_t)&sharp_memory_ls044q7dh01_mode},
434	{},
435};
436MODULE_DEVICE_TABLE(spi, sharp_memory_ids);
437
438static const struct of_device_id sharp_memory_of_match[] = {
439	{.compatible = "sharp,ls010b7dh04", &sharp_memory_ls010b7dh04_mode},
440	{.compatible = "sharp,ls011b7dh03", &sharp_memory_ls011b7dh03_mode},
441	{.compatible = "sharp,ls012b7dd01", &sharp_memory_ls012b7dd01_mode},
442	{.compatible = "sharp,ls013b7dh03", &sharp_memory_ls013b7dh03_mode},
443	{.compatible = "sharp,ls013b7dh05", &sharp_memory_ls013b7dh05_mode},
444	{.compatible = "sharp,ls018b7dh02", &sharp_memory_ls018b7dh02_mode},
445	{.compatible = "sharp,ls027b7dh01", &sharp_memory_ls027b7dh01_mode},
446	{.compatible = "sharp,ls027b7dh01a", &sharp_memory_ls027b7dh01_mode},
447	{.compatible = "sharp,ls032b7dd02", &sharp_memory_ls032b7dd02_mode},
448	{.compatible = "sharp,ls044q7dh01", &sharp_memory_ls044q7dh01_mode},
449	{},
450};
451MODULE_DEVICE_TABLE(of, sharp_memory_of_match);
452
453static const u32 sharp_memory_formats[] = {
454	DRM_FORMAT_XRGB8888,
455};
456
457static int sharp_memory_pipe_init(struct drm_device *dev,
458				  struct sharp_memory_device *smd,
459				  const u32 *formats, unsigned int format_count,
460				  const u64 *format_modifiers)
461{
462	int ret;
463	struct drm_encoder *encoder = &smd->encoder;
464	struct drm_plane *plane = &smd->plane;
465	struct drm_crtc *crtc = &smd->crtc;
466	struct drm_connector *connector = &smd->connector;
467
468	drm_plane_helper_add(plane, &sharp_memory_plane_helper_funcs);
469	ret = drm_universal_plane_init(dev, plane, 0,
470				       &sharp_memory_plane_funcs,
471				       formats, format_count,
472				       format_modifiers,
473				       DRM_PLANE_TYPE_PRIMARY, NULL);
474	if (ret)
475		return ret;
476
477	drm_crtc_helper_add(crtc, &sharp_memory_crtc_helper_funcs);
478	ret = drm_crtc_init_with_planes(dev, crtc, plane, NULL,
479					&sharp_memory_crtc_funcs, NULL);
480	if (ret)
481		return ret;
482
483	encoder->possible_crtcs = drm_crtc_mask(crtc);
484	ret = drm_encoder_init(dev, encoder, &sharp_memory_encoder_funcs,
485			       DRM_MODE_ENCODER_NONE, NULL);
486	if (ret)
487		return ret;
488
489	ret = drm_connector_init(&smd->drm, &smd->connector,
490				 &sharp_memory_connector_funcs,
491				 DRM_MODE_CONNECTOR_SPI);
492	if (ret)
493		return ret;
494
495	drm_connector_helper_add(&smd->connector,
496				 &sharp_memory_connector_hfuncs);
497
498	return drm_connector_attach_encoder(connector, encoder);
499}
500
501static int sharp_memory_init_pwm_vcom_signal(struct sharp_memory_device *smd)
502{
503	int ret;
504	struct device *dev = &smd->spi->dev;
505	struct pwm_state pwm_state;
506
507	smd->pwm_vcom_signal = devm_pwm_get(dev, NULL);
508	if (IS_ERR(smd->pwm_vcom_signal))
509		return dev_err_probe(dev, PTR_ERR(smd->pwm_vcom_signal),
510				     "Could not get pwm device\n");
511
512	pwm_init_state(smd->pwm_vcom_signal, &pwm_state);
513	pwm_set_relative_duty_cycle(&pwm_state, 1, 10);
514	pwm_state.enabled = true;
515	ret = pwm_apply_might_sleep(smd->pwm_vcom_signal, &pwm_state);
516	if (ret)
517		return dev_err_probe(dev, -EINVAL, "Could not apply pwm state\n");
518
519	return 0;
520}
521
522static int sharp_memory_probe(struct spi_device *spi)
523{
524	int ret;
525	struct device *dev;
526	struct sharp_memory_device *smd;
527	struct drm_device *drm;
528	const char *vcom_mode_str;
529
530	dev = &spi->dev;
531
532	ret = spi_setup(spi);
533	if (ret < 0)
534		return dev_err_probe(dev, ret, "Failed to setup spi device\n");
535
536	if (!dev->coherent_dma_mask) {
537		ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(32));
538		if (ret)
539			return dev_err_probe(dev, ret, "Failed to set dma mask\n");
540	}
541
542	smd = devm_drm_dev_alloc(dev, &sharp_memory_drm_driver,
543				 struct sharp_memory_device, drm);
544	if (IS_ERR(smd))
545		return PTR_ERR(smd);
546
547	spi_set_drvdata(spi, smd);
548
549	smd->spi = spi;
550	drm = &smd->drm;
551	ret = drmm_mode_config_init(drm);
552	if (ret)
553		return dev_err_probe(dev, ret, "Failed to initialize drm config\n");
554
555	smd->enable_gpio = devm_gpiod_get_optional(dev, "enable", GPIOD_OUT_HIGH);
556	if (!smd->enable_gpio)
557		dev_warn(dev, "Enable gpio not defined\n");
558
559	drm->mode_config.funcs = &sharp_memory_mode_config_funcs;
560	smd->mode = spi_get_device_match_data(spi);
561
562	smd->pitch = (SHARP_ADDR_PERIOD + smd->mode->hdisplay + SHARP_DUMMY_PERIOD) / 8;
563	smd->tx_buffer_size = (SHARP_MODE_PERIOD +
564			       (SHARP_ADDR_PERIOD + (smd->mode->hdisplay) + SHARP_DUMMY_PERIOD) *
565			       smd->mode->vdisplay) / 8;
566
567	smd->tx_buffer = devm_kzalloc(dev, smd->tx_buffer_size, GFP_KERNEL);
568	if (!smd->tx_buffer)
569		return -ENOMEM;
570
571	mutex_init(&smd->tx_mutex);
572
573	/*
574	 * VCOM is a signal that prevents DC bias from being built up in
575	 * the panel resulting in pixels being forever stuck in one state.
576	 *
577	 * This driver supports three different methods to generate this
578	 * signal depending on EXTMODE pin:
579	 *
580	 * software (EXTMODE = L) - This mode uses a kthread to
581	 * periodically send a "maintain display" message to the display,
582	 * toggling the vcom bit on and off with each message
583	 *
584	 * external (EXTMODE = H) - This mode relies on an external
585	 * clock to generate the signal on the EXTCOMM pin
586	 *
587	 * pwm (EXTMODE = H) - This mode uses a pwm device to generate
588	 * the signal on the EXTCOMM pin
589	 *
590	 */
591	if (device_property_read_string(dev, "sharp,vcom-mode", &vcom_mode_str))
592		return dev_err_probe(dev, -EINVAL,
593				     "Unable to find sharp,vcom-mode node in device tree\n");
594
595	if (!strcmp("software", vcom_mode_str)) {
596		smd->vcom_mode = SHARP_MEMORY_SOFTWARE_VCOM;
597		smd->sw_vcom_signal = kthread_run(sharp_memory_sw_vcom_signal_thread,
598						  smd, "sw_vcom_signal");
599
600	} else if (!strcmp("external", vcom_mode_str)) {
601		smd->vcom_mode = SHARP_MEMORY_EXTERNAL_VCOM;
602
603	} else if (!strcmp("pwm", vcom_mode_str)) {
604		smd->vcom_mode = SHARP_MEMORY_PWM_VCOM;
605		ret = sharp_memory_init_pwm_vcom_signal(smd);
606		if (ret)
607			return ret;
608	} else {
609		return dev_err_probe(dev, -EINVAL, "Invalid value set for vcom-mode\n");
610	}
611
612	drm->mode_config.min_width = smd->mode->hdisplay;
613	drm->mode_config.max_width = smd->mode->hdisplay;
614	drm->mode_config.min_height = smd->mode->vdisplay;
615	drm->mode_config.max_height = smd->mode->vdisplay;
616
617	ret = sharp_memory_pipe_init(drm, smd, sharp_memory_formats,
618				     ARRAY_SIZE(sharp_memory_formats),
619				     NULL);
620	if (ret)
621		return dev_err_probe(dev, ret, "Failed to initialize display pipeline.\n");
622
623	drm_plane_enable_fb_damage_clips(&smd->plane);
624	drm_mode_config_reset(drm);
625
626	ret = drm_dev_register(drm, 0);
627	if (ret)
628		return dev_err_probe(dev, ret, "Failed to register drm device.\n");
629
630	drm_client_setup(drm, NULL);
631
632	return 0;
633}
634
635static void sharp_memory_remove(struct spi_device *spi)
636{
637	struct sharp_memory_device *smd = spi_get_drvdata(spi);
638
639	drm_dev_unplug(&smd->drm);
640	drm_atomic_helper_shutdown(&smd->drm);
641
642	switch (smd->vcom_mode) {
643	case SHARP_MEMORY_SOFTWARE_VCOM:
644		kthread_stop(smd->sw_vcom_signal);
645		break;
646
647	case SHARP_MEMORY_EXTERNAL_VCOM:
648		break;
649
650	case SHARP_MEMORY_PWM_VCOM:
651		pwm_disable(smd->pwm_vcom_signal);
652		break;
653	}
654}
655
656static struct spi_driver sharp_memory_spi_driver = {
657	.driver = {
658		.name = "sharp_memory",
659		.of_match_table = sharp_memory_of_match,
660	},
661	.probe = sharp_memory_probe,
662	.remove = sharp_memory_remove,
663	.id_table = sharp_memory_ids,
664};
665module_spi_driver(sharp_memory_spi_driver);
666
667MODULE_AUTHOR("Alex Lanzano <lanzano.alex@gmail.com>");
668MODULE_DESCRIPTION("SPI Protocol driver for the sharp_memory display");
669MODULE_LICENSE("GPL");
~/ Read-only file preview.