static void panel_destroy(struct tilcdc_module *mod)
{
	struct panel_module *panel_mod = to_panel_module(mod);

	if (panel_mod->timings)
		display_timings_release(panel_mod->timings);

	tilcdc_module_cleanup(mod);
	kfree(panel_mod->info);
	kfree(panel_mod);
}
static int panel_remove(struct platform_device *pdev)
{
	struct tilcdc_module *mod = dev_get_platdata(&pdev->dev);
	struct panel_module *panel_mod = to_panel_module(mod);
	struct backlight_device *backlight = panel_mod->backlight;

	if (backlight)
		put_device(&backlight->dev);

	display_timings_release(panel_mod->timings);

	tilcdc_module_cleanup(mod);
	kfree(panel_mod->info);

	return 0;
}
/**
 * of_get_display_timings - parse all display_timing entries from a device_node
 * @np: device_node with the subnodes
 **/
struct display_timings *of_get_display_timings(struct device_node *np)
{
	struct device_node *timings_np;
	struct device_node *entry;
	struct device_node *native_mode;
	struct display_timings *disp;

	if (!np) {
		pr_err("%s: no devicenode given\n", of_node_full_name(np));
		return NULL;
	}

	timings_np = of_find_node_by_name(np, "display-timings");
	if (!timings_np) {
		pr_err("%s: could not find display-timings node\n",
			of_node_full_name(np));
		return NULL;
	}

	disp = kzalloc(sizeof(*disp), GFP_KERNEL);
	if (!disp) {
		pr_err("%s: could not allocate struct disp'\n",
			of_node_full_name(np));
		goto dispfail;
	}

	entry = of_parse_phandle(timings_np, "native-mode", 0);
	/* assume first child as native mode if none provided */
	if (!entry)
		entry = of_get_next_child(np, NULL);
	/* if there is no child, it is useless to go on */
	if (!entry) {
		pr_err("%s: no timing specifications given\n",
			of_node_full_name(np));
		goto entryfail;
	}

	pr_debug("%s: using %s as default timing\n",
		of_node_full_name(np), entry->name);

	native_mode = entry;

	disp->num_timings = of_get_child_count(timings_np);
	if (disp->num_timings == 0) {
		/* should never happen, as entry was already found above */
		pr_err("%s: no timings specified\n", of_node_full_name(np));
		goto entryfail;
	}

	disp->timings = kzalloc(sizeof(struct display_timing *) *
				disp->num_timings, GFP_KERNEL);
	if (!disp->timings) {
		pr_err("%s: could not allocate timings array\n",
			of_node_full_name(np));
		goto entryfail;
	}

	disp->num_timings = 0;
	disp->native_mode = 0;

	for_each_child_of_node(timings_np, entry) {
		struct display_timing *dt;

		dt = of_get_display_timing(entry);
		if (!dt) {
			/*
			 * to not encourage wrong devicetrees, fail in case of
			 * an error
			 */
			pr_err("%s: error in timing %d\n",
				of_node_full_name(np), disp->num_timings + 1);
			goto timingfail;
		}

		if (native_mode == entry)
			disp->native_mode = disp->num_timings;

		disp->timings[disp->num_timings] = dt;
		disp->num_timings++;
	}
	of_node_put(timings_np);
	/*
	 * native_mode points to the device_node returned by of_parse_phandle
	 * therefore call of_node_put on it
	 */
	of_node_put(native_mode);

	pr_debug("%s: got %d timings. Using timing #%d as default\n",
		of_node_full_name(np), disp->num_timings,
		disp->native_mode + 1);

	return disp;

timingfail:
	if (native_mode)
		of_node_put(native_mode);
	display_timings_release(disp);
entryfail:
	kfree(disp);
dispfail:
	of_node_put(timings_np);
	return NULL;
}
static int panel_probe(struct platform_device *pdev)
{
	struct device_node *bl_node, *node = pdev->dev.of_node;
	struct panel_module *panel_mod;
	struct tilcdc_module *mod;
	struct pinctrl *pinctrl;
	int ret;

	/* bail out early if no DT data: */
	if (!node) {
		dev_err(&pdev->dev, "device-tree data is missing\n");
		return -ENXIO;
	}

	panel_mod = devm_kzalloc(&pdev->dev, sizeof(*panel_mod), GFP_KERNEL);
	if (!panel_mod)
		return -ENOMEM;

	bl_node = of_parse_phandle(node, "backlight", 0);
	if (bl_node) {
		panel_mod->backlight = of_find_backlight_by_node(bl_node);
		of_node_put(bl_node);

		if (!panel_mod->backlight)
			return -EPROBE_DEFER;

		dev_info(&pdev->dev, "found backlight\n");
	}

	panel_mod->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable",
							 GPIOD_OUT_LOW);
	if (IS_ERR(panel_mod->enable_gpio)) {
		ret = PTR_ERR(panel_mod->enable_gpio);
		dev_err(&pdev->dev, "failed to request enable GPIO\n");
		goto fail_backlight;
	}

	if (panel_mod->enable_gpio)
		dev_info(&pdev->dev, "found enable GPIO\n");

	mod = &panel_mod->base;
	pdev->dev.platform_data = mod;

	tilcdc_module_init(mod, "panel", &panel_module_ops);

	pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
	if (IS_ERR(pinctrl))
		dev_warn(&pdev->dev, "pins are not configured\n");

	panel_mod->timings = of_get_display_timings(node);
	if (!panel_mod->timings) {
		dev_err(&pdev->dev, "could not get panel timings\n");
		ret = -EINVAL;
		goto fail_free;
	}

	panel_mod->info = of_get_panel_info(node);
	if (!panel_mod->info) {
		dev_err(&pdev->dev, "could not get panel info\n");
		ret = -EINVAL;
		goto fail_timings;
	}

	mod->preferred_bpp = panel_mod->info->bpp;

	return 0;

fail_timings:
	display_timings_release(panel_mod->timings);

fail_free:
	tilcdc_module_cleanup(mod);

fail_backlight:
	if (panel_mod->backlight)
		put_device(&panel_mod->backlight->dev);
	return ret;
}