Linux Audio

Check our new training course

Loading...
  1/*
  2 * Copyright © 2010 Intel Corporation
  3 *
  4 * Permission is hereby granted, free of charge, to any person obtaining a
  5 * copy of this software and associated documentation files (the "Software"),
  6 * to deal in the Software without restriction, including without limitation
  7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8 * and/or sell copies of the Software, and to permit persons to whom the
  9 * Software is furnished to do so, subject to the following conditions:
 10 *
 11 * The above copyright notice and this permission notice (including the next
 12 * paragraph) shall be included in all copies or substantial portions of the
 13 * Software.
 14 *
 15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 21 * DEALINGS IN THE SOFTWARE.
 22 *
 23 * Authors:
 24 * jim liu <jim.liu@intel.com>
 25 * Jackie Li<yaodong.li@intel.com>
 26 */
 27
 28#include <linux/module.h>
 29
 30#include "mdfld_dsi_output.h"
 31#include "mdfld_dsi_dpi.h"
 32#include "mdfld_output.h"
 33#include "mdfld_dsi_pkg_sender.h"
 34#include "tc35876x-dsi-lvds.h"
 35#include <linux/pm_runtime.h>
 36#include <asm/intel_scu_ipc.h>
 37
 38/* get the LABC from command line. */
 39static int LABC_control = 1;
 40
 41#ifdef MODULE
 42module_param(LABC_control, int, 0644);
 43#else
 44
 45static int __init parse_LABC_control(char *arg)
 46{
 47	/* LABC control can be passed in as a cmdline parameter */
 48	/* to enable this feature add LABC=1 to cmdline */
 49	/* to disable this feature add LABC=0 to cmdline */
 50	if (!arg)
 51		return -EINVAL;
 52
 53	if (!strcasecmp(arg, "0"))
 54		LABC_control = 0;
 55	else if (!strcasecmp(arg, "1"))
 56		LABC_control = 1;
 57
 58	return 0;
 59}
 60early_param("LABC", parse_LABC_control);
 61#endif
 62
 63/**
 64 * Check and see if the generic control or data buffer is empty and ready.
 65 */
 66void mdfld_dsi_gen_fifo_ready(struct drm_device *dev, u32 gen_fifo_stat_reg,
 67							u32 fifo_stat)
 68{
 69	u32 GEN_BF_time_out_count;
 70
 71	/* Check MIPI Adatper command registers */
 72	for (GEN_BF_time_out_count = 0;
 73			GEN_BF_time_out_count < GEN_FB_TIME_OUT;
 74			GEN_BF_time_out_count++) {
 75		if ((REG_READ(gen_fifo_stat_reg) & fifo_stat) == fifo_stat)
 76			break;
 77		udelay(100);
 78	}
 79
 80	if (GEN_BF_time_out_count == GEN_FB_TIME_OUT)
 81		DRM_ERROR("mdfld_dsi_gen_fifo_ready, Timeout. gen_fifo_stat_reg = 0x%x.\n",
 82					gen_fifo_stat_reg);
 83}
 84
 85/**
 86 * Manage the DSI MIPI keyboard and display brightness.
 87 * FIXME: this is exported to OSPM code. should work out an specific
 88 * display interface to OSPM.
 89 */
 90
 91void mdfld_dsi_brightness_init(struct mdfld_dsi_config *dsi_config, int pipe)
 92{
 93	struct mdfld_dsi_pkg_sender *sender =
 94				mdfld_dsi_get_pkg_sender(dsi_config);
 95	struct drm_device *dev = sender->dev;
 96	struct drm_psb_private *dev_priv = dev->dev_private;
 97	u32 gen_ctrl_val;
 98
 99	if (!sender) {
100		DRM_ERROR("No sender found\n");
101		return;
102	}
103
104	/* Set default display backlight value to 85% (0xd8)*/
105	mdfld_dsi_send_mcs_short(sender, write_display_brightness, 0xd8, 1,
106				true);
107
108	/* Set minimum brightness setting of CABC function to 20% (0x33)*/
109	mdfld_dsi_send_mcs_short(sender, write_cabc_min_bright, 0x33, 1, true);
110
111	/* Enable backlight or/and LABC */
112	gen_ctrl_val = BRIGHT_CNTL_BLOCK_ON | DISPLAY_DIMMING_ON |
113								BACKLIGHT_ON;
114	if (LABC_control == 1)
115		gen_ctrl_val |= DISPLAY_DIMMING_ON | DISPLAY_BRIGHTNESS_AUTO
116								| GAMMA_AUTO;
117
118	if (LABC_control == 1)
119		gen_ctrl_val |= AMBIENT_LIGHT_SENSE_ON;
120
121	dev_priv->mipi_ctrl_display = gen_ctrl_val;
122
123	mdfld_dsi_send_mcs_short(sender, write_ctrl_display, (u8)gen_ctrl_val,
124				1, true);
125
126	mdfld_dsi_send_mcs_short(sender, write_ctrl_cabc, UI_IMAGE, 1, true);
127}
128
129void mdfld_dsi_brightness_control(struct drm_device *dev, int pipe, int level)
130{
131	struct mdfld_dsi_pkg_sender *sender;
132	struct drm_psb_private *dev_priv;
133	struct mdfld_dsi_config *dsi_config;
134	u32 gen_ctrl_val = 0;
135	int p_type = TMD_VID;
136
137	if (!dev || (pipe != 0 && pipe != 2)) {
138		DRM_ERROR("Invalid parameter\n");
139		return;
140	}
141
142	p_type = mdfld_get_panel_type(dev, 0);
143
144	dev_priv = dev->dev_private;
145
146	if (pipe)
147		dsi_config = dev_priv->dsi_configs[1];
148	else
149		dsi_config = dev_priv->dsi_configs[0];
150
151	sender = mdfld_dsi_get_pkg_sender(dsi_config);
152
153	if (!sender) {
154		DRM_ERROR("No sender found\n");
155		return;
156	}
157
158	gen_ctrl_val = (level * 0xff / MDFLD_DSI_BRIGHTNESS_MAX_LEVEL) & 0xff;
159
160	dev_dbg(sender->dev->dev, "pipe = %d, gen_ctrl_val = %d.\n",
161							pipe, gen_ctrl_val);
162
163	if (p_type == TMD_VID) {
164		/* Set display backlight value */
165		mdfld_dsi_send_mcs_short(sender, tmd_write_display_brightness,
166					(u8)gen_ctrl_val, 1, true);
167	} else {
168		/* Set display backlight value */
169		mdfld_dsi_send_mcs_short(sender, write_display_brightness,
170					(u8)gen_ctrl_val, 1, true);
171
172		/* Enable backlight control */
173		if (level == 0)
174			gen_ctrl_val = 0;
175		else
176			gen_ctrl_val = dev_priv->mipi_ctrl_display;
177
178		mdfld_dsi_send_mcs_short(sender, write_ctrl_display,
179					(u8)gen_ctrl_val, 1, true);
180	}
181}
182
183static int mdfld_dsi_get_panel_status(struct mdfld_dsi_config *dsi_config,
184				u8 dcs, u32 *data, bool hs)
185{
186	struct mdfld_dsi_pkg_sender *sender
187		= mdfld_dsi_get_pkg_sender(dsi_config);
188
189	if (!sender || !data) {
190		DRM_ERROR("Invalid parameter\n");
191		return -EINVAL;
192	}
193
194	return mdfld_dsi_read_mcs(sender, dcs, data, 1, hs);
195}
196
197int mdfld_dsi_get_power_mode(struct mdfld_dsi_config *dsi_config, u32 *mode,
198			bool hs)
199{
200	if (!dsi_config || !mode) {
201		DRM_ERROR("Invalid parameter\n");
202		return -EINVAL;
203	}
204
205	return mdfld_dsi_get_panel_status(dsi_config, 0x0a, mode, hs);
206}
207
208/*
209 * NOTE: this function was used by OSPM.
210 * TODO: will be removed later, should work out display interfaces for OSPM
211 */
212void mdfld_dsi_controller_init(struct mdfld_dsi_config *dsi_config, int pipe)
213{
214	if (!dsi_config || ((pipe != 0) && (pipe != 2))) {
215		DRM_ERROR("Invalid parameters\n");
216		return;
217	}
218
219	mdfld_dsi_dpi_controller_init(dsi_config, pipe);
220}
221
222static void mdfld_dsi_connector_save(struct drm_connector *connector)
223{
224}
225
226static void mdfld_dsi_connector_restore(struct drm_connector *connector)
227{
228}
229
230/* FIXME: start using the force parameter */
231static enum drm_connector_status
232mdfld_dsi_connector_detect(struct drm_connector *connector, bool force)
233{
234	struct mdfld_dsi_connector *dsi_connector
235		= mdfld_dsi_connector(connector);
236
237	dsi_connector->status = connector_status_connected;
238
239	return dsi_connector->status;
240}
241
242static int mdfld_dsi_connector_set_property(struct drm_connector *connector,
243				struct drm_property *property,
244				uint64_t value)
245{
246	struct drm_encoder *encoder = connector->encoder;
247
248	if (!strcmp(property->name, "scaling mode") && encoder) {
249		struct psb_intel_crtc *psb_crtc =
250					to_psb_intel_crtc(encoder->crtc);
251		bool centerechange;
252		uint64_t val;
253
254		if (!psb_crtc)
255			goto set_prop_error;
256
257		switch (value) {
258		case DRM_MODE_SCALE_FULLSCREEN:
259			break;
260		case DRM_MODE_SCALE_NO_SCALE:
261			break;
262		case DRM_MODE_SCALE_ASPECT:
263			break;
264		default:
265			goto set_prop_error;
266		}
267
268		if (drm_connector_property_get_value(connector, property, &val))
269			goto set_prop_error;
270
271		if (val == value)
272			goto set_prop_done;
273
274		if (drm_connector_property_set_value(connector,
275							property, value))
276			goto set_prop_error;
277
278		centerechange = (val == DRM_MODE_SCALE_NO_SCALE) ||
279			(value == DRM_MODE_SCALE_NO_SCALE);
280
281		if (psb_crtc->saved_mode.hdisplay != 0 &&
282		    psb_crtc->saved_mode.vdisplay != 0) {
283			if (centerechange) {
284				if (!drm_crtc_helper_set_mode(encoder->crtc,
285						&psb_crtc->saved_mode,
286						encoder->crtc->x,
287						encoder->crtc->y,
288						encoder->crtc->fb))
289					goto set_prop_error;
290			} else {
291				struct drm_encoder_helper_funcs *funcs =
292						encoder->helper_private;
293				funcs->mode_set(encoder,
294					&psb_crtc->saved_mode,
295					&psb_crtc->saved_adjusted_mode);
296			}
297		}
298	} else if (!strcmp(property->name, "backlight") && encoder) {
299		if (drm_connector_property_set_value(connector, property,
300									value))
301			goto set_prop_error;
302		else {
303#ifdef CONFIG_BACKLIGHT_CLASS_DEVICE
304			struct backlight_device *psb_bd;
305
306			psb_bd = mdfld_get_backlight_device();
307			if (psb_bd) {
308				psb_bd->props.brightness = value;
309				mdfld_set_brightness(psb_bd);
310			}
311#endif
312		}
313	}
314set_prop_done:
315	return 0;
316set_prop_error:
317	return -1;
318}
319
320static void mdfld_dsi_connector_destroy(struct drm_connector *connector)
321{
322	struct mdfld_dsi_connector *dsi_connector =
323					mdfld_dsi_connector(connector);
324	struct mdfld_dsi_pkg_sender *sender;
325
326	if (!dsi_connector)
327		return;
328	drm_sysfs_connector_remove(connector);
329	drm_connector_cleanup(connector);
330	sender = dsi_connector->pkg_sender;
331	mdfld_dsi_pkg_sender_destroy(sender);
332	kfree(dsi_connector);
333}
334
335static int mdfld_dsi_connector_get_modes(struct drm_connector *connector)
336{
337	struct mdfld_dsi_connector *dsi_connector =
338				mdfld_dsi_connector(connector);
339	struct mdfld_dsi_config *dsi_config =
340				mdfld_dsi_get_config(dsi_connector);
341	struct drm_display_mode *fixed_mode = dsi_config->fixed_mode;
342	struct drm_display_mode *dup_mode = NULL;
343	struct drm_device *dev = connector->dev;
344
345	connector->display_info.min_vfreq = 0;
346	connector->display_info.max_vfreq = 200;
347	connector->display_info.min_hfreq = 0;
348	connector->display_info.max_hfreq = 200;
349
350	if (fixed_mode) {
351		dev_dbg(dev->dev, "fixed_mode %dx%d\n",
352				fixed_mode->hdisplay, fixed_mode->vdisplay);
353		dup_mode = drm_mode_duplicate(dev, fixed_mode);
354		drm_mode_probed_add(connector, dup_mode);
355		return 1;
356	}
357	DRM_ERROR("Didn't get any modes!\n");
358	return 0;
359}
360
361static int mdfld_dsi_connector_mode_valid(struct drm_connector *connector,
362						struct drm_display_mode *mode)
363{
364	struct mdfld_dsi_connector *dsi_connector =
365					mdfld_dsi_connector(connector);
366	struct mdfld_dsi_config *dsi_config =
367					mdfld_dsi_get_config(dsi_connector);
368	struct drm_display_mode *fixed_mode = dsi_config->fixed_mode;
369
370	if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
371		return MODE_NO_DBLESCAN;
372
373	if (mode->flags & DRM_MODE_FLAG_INTERLACE)
374		return MODE_NO_INTERLACE;
375
376	/**
377	 * FIXME: current DC has no fitting unit, reject any mode setting
378	 * request
379	 * Will figure out a way to do up-scaling(pannel fitting) later.
380	 **/
381	if (fixed_mode) {
382		if (mode->hdisplay != fixed_mode->hdisplay)
383			return MODE_PANEL;
384
385		if (mode->vdisplay != fixed_mode->vdisplay)
386			return MODE_PANEL;
387	}
388
389	return MODE_OK;
390}
391
392static void mdfld_dsi_connector_dpms(struct drm_connector *connector, int mode)
393{
394	if (mode == connector->dpms)
395		return;
396
397	/*first, execute dpms*/
398
399	drm_helper_connector_dpms(connector, mode);
400}
401
402static struct drm_encoder *mdfld_dsi_connector_best_encoder(
403				struct drm_connector *connector)
404{
405	struct mdfld_dsi_connector *dsi_connector =
406				mdfld_dsi_connector(connector);
407	struct mdfld_dsi_config *dsi_config =
408				mdfld_dsi_get_config(dsi_connector);
409	return &dsi_config->encoder->base.base;
410}
411
412/*DSI connector funcs*/
413static const struct drm_connector_funcs mdfld_dsi_connector_funcs = {
414	.dpms = /*drm_helper_connector_dpms*/mdfld_dsi_connector_dpms,
415	.save = mdfld_dsi_connector_save,
416	.restore = mdfld_dsi_connector_restore,
417	.detect = mdfld_dsi_connector_detect,
418	.fill_modes = drm_helper_probe_single_connector_modes,
419	.set_property = mdfld_dsi_connector_set_property,
420	.destroy = mdfld_dsi_connector_destroy,
421};
422
423/*DSI connector helper funcs*/
424static const struct drm_connector_helper_funcs
425	mdfld_dsi_connector_helper_funcs = {
426	.get_modes = mdfld_dsi_connector_get_modes,
427	.mode_valid = mdfld_dsi_connector_mode_valid,
428	.best_encoder = mdfld_dsi_connector_best_encoder,
429};
430
431static int mdfld_dsi_get_default_config(struct drm_device *dev,
432				struct mdfld_dsi_config *config, int pipe)
433{
434	if (!dev || !config) {
435		DRM_ERROR("Invalid parameters");
436		return -EINVAL;
437	}
438
439	config->bpp = 24;
440	if (mdfld_get_panel_type(dev, pipe) == TC35876X)
441		config->lane_count = 4;
442	else
443		config->lane_count = 2;
444	config->channel_num = 0;
445
446	if (mdfld_get_panel_type(dev, pipe) == TMD_VID)
447		config->video_mode = MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_PULSE;
448	else if (mdfld_get_panel_type(dev, pipe) == TC35876X)
449		config->video_mode =
450				MDFLD_DSI_VIDEO_NON_BURST_MODE_SYNC_EVENTS;
451	else
452		config->video_mode = MDFLD_DSI_VIDEO_BURST_MODE;
453
454	return 0;
455}
456
457int mdfld_dsi_panel_reset(int pipe)
458{
459	unsigned gpio;
460	int ret = 0;
461
462	switch (pipe) {
463	case 0:
464		gpio = 128;
465		break;
466	case 2:
467		gpio = 34;
468		break;
469	default:
470		DRM_ERROR("Invalid output\n");
471		return -EINVAL;
472	}
473
474	ret = gpio_request(gpio, "gfx");
475	if (ret) {
476		DRM_ERROR("gpio_rqueset failed\n");
477		return ret;
478	}
479
480	ret = gpio_direction_output(gpio, 1);
481	if (ret) {
482		DRM_ERROR("gpio_direction_output failed\n");
483		goto gpio_error;
484	}
485
486	gpio_get_value(128);
487
488gpio_error:
489	if (gpio_is_valid(gpio))
490		gpio_free(gpio);
491
492	return ret;
493}
494
495/*
496 * MIPI output init
497 * @dev drm device
498 * @pipe pipe number. 0 or 2
499 * @config
500 *
501 * Do the initialization of a MIPI output, including create DRM mode objects
502 * initialization of DSI output on @pipe
503 */
504void mdfld_dsi_output_init(struct drm_device *dev,
505			   int pipe,
506			   const struct panel_funcs *p_vid_funcs)
507{
508	struct mdfld_dsi_config *dsi_config;
509	struct mdfld_dsi_connector *dsi_connector;
510	struct drm_connector *connector;
511	struct mdfld_dsi_encoder *encoder;
512	struct drm_psb_private *dev_priv = dev->dev_private;
513	struct panel_info dsi_panel_info;
514	u32 width_mm, height_mm;
515
516	dev_dbg(dev->dev, "init DSI output on pipe %d\n", pipe);
517
518	if (!dev || ((pipe != 0) && (pipe != 2))) {
519		DRM_ERROR("Invalid parameter\n");
520		return;
521	}
522
523	/*create a new connetor*/
524	dsi_connector = kzalloc(sizeof(struct mdfld_dsi_connector), GFP_KERNEL);
525	if (!dsi_connector) {
526		DRM_ERROR("No memory");
527		return;
528	}
529
530	dsi_connector->pipe =  pipe;
531
532	dsi_config = kzalloc(sizeof(struct mdfld_dsi_config),
533			GFP_KERNEL);
534	if (!dsi_config) {
535		DRM_ERROR("cannot allocate memory for DSI config\n");
536		goto dsi_init_err0;
537	}
538	mdfld_dsi_get_default_config(dev, dsi_config, pipe);
539
540	dsi_connector->private = dsi_config;
541
542	dsi_config->changed = 1;
543	dsi_config->dev = dev;
544
545	dsi_config->fixed_mode = p_vid_funcs->get_config_mode(dev);
546	if (p_vid_funcs->get_panel_info(dev, pipe, &dsi_panel_info))
547			goto dsi_init_err0;
548
549	width_mm = dsi_panel_info.width_mm;
550	height_mm = dsi_panel_info.height_mm;
551
552	dsi_config->mode = dsi_config->fixed_mode;
553	dsi_config->connector = dsi_connector;
554
555	if (!dsi_config->fixed_mode) {
556		DRM_ERROR("No pannel fixed mode was found\n");
557		goto dsi_init_err0;
558	}
559
560	if (pipe && dev_priv->dsi_configs[0]) {
561		dsi_config->dvr_ic_inited = 0;
562		dev_priv->dsi_configs[1] = dsi_config;
563	} else if (pipe == 0) {
564		dsi_config->dvr_ic_inited = 1;
565		dev_priv->dsi_configs[0] = dsi_config;
566	} else {
567		DRM_ERROR("Trying to init MIPI1 before MIPI0\n");
568		goto dsi_init_err0;
569	}
570
571
572	connector = &dsi_connector->base.base;
573	drm_connector_init(dev, connector, &mdfld_dsi_connector_funcs,
574						DRM_MODE_CONNECTOR_LVDS);
575	drm_connector_helper_add(connector, &mdfld_dsi_connector_helper_funcs);
576
577	connector->display_info.subpixel_order = SubPixelHorizontalRGB;
578	connector->display_info.width_mm = width_mm;
579	connector->display_info.height_mm = height_mm;
580	connector->interlace_allowed = false;
581	connector->doublescan_allowed = false;
582
583	/*attach properties*/
584	drm_connector_attach_property(connector,
585				dev->mode_config.scaling_mode_property,
586				DRM_MODE_SCALE_FULLSCREEN);
587	drm_connector_attach_property(connector,
588				dev_priv->backlight_property,
589				MDFLD_DSI_BRIGHTNESS_MAX_LEVEL);
590
591	/*init DSI package sender on this output*/
592	if (mdfld_dsi_pkg_sender_init(dsi_connector, pipe)) {
593		DRM_ERROR("Package Sender initialization failed on pipe %d\n",
594									pipe);
595		goto dsi_init_err0;
596	}
597
598	encoder = mdfld_dsi_dpi_init(dev, dsi_connector, p_vid_funcs);
599	if (!encoder) {
600		DRM_ERROR("Create DPI encoder failed\n");
601		goto dsi_init_err1;
602	}
603	encoder->private = dsi_config;
604	dsi_config->encoder = encoder;
605	encoder->base.type = (pipe == 0) ? INTEL_OUTPUT_MIPI :
606		INTEL_OUTPUT_MIPI2;
607	drm_sysfs_connector_add(connector);
608	return;
609
610	/*TODO: add code to destroy outputs on error*/
611dsi_init_err1:
612	/*destroy sender*/
613	mdfld_dsi_pkg_sender_destroy(dsi_connector->pkg_sender);
614
615	drm_connector_cleanup(connector);
616
617	kfree(dsi_config->fixed_mode);
618	kfree(dsi_config);
619dsi_init_err0:
620	kfree(dsi_connector);
621}