天天看點

賽靈思xilinx平台drm分析

不管什麼裝置輸出,使用drm架構,都要做幾個步驟:fb、crtc、plane、encoder和connector初始化;

以xilinx異構平台設計在FPGA端的HDMI為例,跟讀代碼分析:

1.HDMI驅動子產品

這裡涉及到就是encoder和connector的初始化:

static int xlnx_drm_hdmi_bind(struct device *dev, struct device *master,
			void *data)
{
	struct xlnx_drm_hdmi *xhdmi = dev_get_drvdata(dev);
	struct drm_encoder *encoder = &xhdmi->encoder;
	struct drm_device *drm_dev = data;
	int ret;

	/*
	 * TODO: The possible CRTCs are 1 now as per current implementation of
	 * HDMI tx driver. DRM framework can support more than one CRTCs and
	 * HDMI driver can be enhanced for that.
	 */
	encoder->possible_crtcs = 1;

	/* initialize encoder */
	drm_encoder_init(drm_dev, encoder, &xlnx_drm_hdmi_encoder_funcs,
			 DRM_MODE_ENCODER_TMDS, NULL);
	drm_encoder_helper_add(encoder, &xlnx_drm_hdmi_encoder_helper_funcs);

	/* create connector */
	ret = xlnx_drm_hdmi_create_connector(encoder);
	if (ret) {
		dev_err(xhdmi->dev, "failed creating connector, ret = %d\n", ret);
		drm_encoder_cleanup(encoder);
	}
	return ret;
}
           
static const struct component_ops xlnx_drm_hdmi_component_ops = {
	.bind	= xlnx_drm_hdmi_bind,
	.unbind	= xlnx_drm_hdmi_unbind
};
           
component_add(xhdmi->dev, &xlnx_drm_hdmi_component_ops);
           

标記1:通過component架構來管理,這裡添加該元件,待平台端比對并擷取;

2.平台端操作:

pl端的裝置樹:

v_drm_dmaengine_drv: drm-dmaengine-drv { 
			compatible = "xlnx,pl-disp"; 
			dmas = <&v_frmbuf_rd 0>; 
			dma-names = "dma0"; 
			xlnx,vformat = "BG24"; 
			#address-cells = <1>;
			#size-cells = <0>;		
			dmaengine_port: [email protected] { 
				reg = <0>; 
				dmaengine_crtc: endpoint { 
					remote-endpoint = <&hdmi_encoder>; 
				}; 
			}; 
		};
           
static int xlnx_pl_disp_probe(struct platform_device *pdev)
{
	struct device *dev = &pdev->dev;
	struct device_node *vtc_node;
	struct xlnx_pl_disp *xlnx_pl_disp;
	int ret;
	const char *vformat;
	struct dma_chan *dma_chan;
	struct xlnx_dma_chan *xlnx_dma_chan;

	xlnx_pl_disp = devm_kzalloc(dev, sizeof(*xlnx_pl_disp), GFP_KERNEL);
	if (!xlnx_pl_disp)
		return -ENOMEM;

	//請求配置設定dma通道
	dma_chan = of_dma_request_slave_channel(dev->of_node, "dma0");
	if (IS_ERR_OR_NULL(dma_chan)) {
		dev_err(dev, "failed to request dma channel\n");
		return PTR_ERR(dma_chan);
	}

	xlnx_dma_chan = devm_kzalloc(dev, sizeof(*xlnx_dma_chan), GFP_KERNEL);
	if (!xlnx_dma_chan)
		return -ENOMEM;

	xlnx_dma_chan->dma_chan = dma_chan;
	xlnx_pl_disp->chan = xlnx_dma_chan;
	ret = of_property_read_string(dev->of_node, "xlnx,vformat", &vformat);
	if (ret) {
		dev_err(dev, "No xlnx,vformat value in dts\n");
		goto err_dma;
	}
	
	strcpy((char *)&xlnx_pl_disp->fmt, vformat);

	/* VTC Bridge support */
	vtc_node = of_parse_phandle(dev->of_node, "xlnx,bridge", 0);
	if (vtc_node) {
		xlnx_pl_disp->vtc_bridge = of_xlnx_bridge_get(vtc_node);
		if (!xlnx_pl_disp->vtc_bridge) {
			dev_info(dev, "Didn't get vtc bridge instance\n");
			return -EPROBE_DEFER;
		}
	} else {
		dev_info(dev, "vtc bridge property not present\n");
	}

	xlnx_pl_disp->dev = dev;
	platform_set_drvdata(pdev, xlnx_pl_disp);

	ret = component_add(dev, &xlnx_pl_disp_component_ops);
	if (ret)
		goto err_dma;

	xlnx_pl_disp->master = xlnx_drm_pipeline_init(pdev);
	if (IS_ERR(xlnx_pl_disp->master)) {
		ret = PTR_ERR(xlnx_pl_disp->master);
		dev_err(dev, "failed to initialize the drm pipeline\n");
		goto err_component;
	}

	dev_info(&pdev->dev, "Xlnx PL display driver probed\n");

	return 0;

err_component:
	component_del(dev, &xlnx_pl_disp_component_ops);
err_dma:
	dma_release_channel(xlnx_pl_disp->chan->dma_chan);

	return ret;
}
           

在這裡同樣可以看到也添加一個元件:

component_add(dev, &xlnx_pl_disp_component_ops);

static const struct component_ops xlnx_pl_disp_component_ops = {
	.bind	= xlnx_pl_disp_bind,
	.unbind	= xlnx_pl_disp_unbind,
};
           
static int xlnx_pl_disp_bind(struct device *dev, struct device *master,
			     void *data)
{
	struct drm_device *drm = data;
	struct xlnx_pl_disp *xlnx_pl_disp = dev_get_drvdata(dev);
	int ret;
	u32 *fmts = NULL;
	unsigned int num_fmts = 0;

	/* in case of fb IP query the supported formats and there count */
	xilinx_xdma_get_drm_vid_fmts(xlnx_pl_disp->chan->dma_chan,
				     &num_fmts, &fmts);
	ret = drm_universal_plane_init(drm, &xlnx_pl_disp->plane, 0,
				       &xlnx_pl_disp_plane_funcs,
				       fmts ? fmts : &xlnx_pl_disp->fmt,
				       num_fmts ? num_fmts : 1,
				       NULL, DRM_PLANE_TYPE_PRIMARY, NULL);
	if (ret)
		return ret;

	drm_plane_helper_add(&xlnx_pl_disp->plane,
			     &xlnx_pl_disp_plane_helper_funcs);

	ret = drm_crtc_init_with_planes(drm, &xlnx_pl_disp->xlnx_crtc.crtc,
					&xlnx_pl_disp->plane, NULL,
					&xlnx_pl_disp_crtc_funcs, NULL);
	if (ret) {
		drm_plane_cleanup(&xlnx_pl_disp->plane);
		return ret;
	}

	drm_crtc_helper_add(&xlnx_pl_disp->xlnx_crtc.crtc,
			    &xlnx_pl_disp_crtc_helper_funcs);
	xlnx_pl_disp->xlnx_crtc.get_format = &xlnx_pl_disp_get_format;
	xlnx_pl_disp->xlnx_crtc.get_align = &xlnx_pl_disp_get_align;
	xlnx_pl_disp->drm = drm;
	xlnx_crtc_register(xlnx_pl_disp->drm, &xlnx_pl_disp->xlnx_crtc);

	return 0;
}
           

其操作就是crtc、plane的初始化;

标記2:那元件集合裡(一個hdmi驅動子產品處添加的,一個平台pl disp添加)是如何去比對配對的呢,在剛才disp_pl添加元件後執行如下函數:

xlnx_pl_disp->master = xlnx_drm_pipeline_init(pdev);

Path: xlnx_drv.c(drivers/gpu/drm/xlnx)

struct platform_device *xlnx_drm_pipeline_init(struct platform_device *pdev)
{
	struct platform_device *master;
	int id, ret;

	id = ffs(xlnx_master_ids);
	if (!id)
		return ERR_PTR(-ENOSPC);

	master = platform_device_alloc("xlnx-drm", id - 1);
	if (!master)
		return ERR_PTR(-ENOMEM);

	master->dev.parent = &pdev->dev;
	ret = platform_device_add(master);
	if (ret)
		goto err_out;

	WARN_ON(master->id != id - 1);
	xlnx_master_ids &= ~BIT(master->id);
	return master;

err_out:
	platform_device_unregister(master);
	return ERR_PTR(ret);
}
           

通過xlnx-drm比對後執行,這裡先說下結果:元件比對後執行所有的bind操作,crtc、plane、encoder、connector都将初始化,

跟讀代碼詳細了解下:

static int xlnx_platform_probe(struct platform_device *pdev)
{
	return xlnx_of_component_probe(&pdev->dev, xlnx_compare_of,
				       &xlnx_master_ops);
}
           
static int xlnx_of_component_probe(struct device *master_dev,
				   int (*compare_of)(struct device *, void *),
				   const struct component_master_ops *m_ops)
{
	struct device *dev = master_dev->parent;
	struct device_node *ep, *port, *remote, *parent;
	struct component_match *match = NULL;
	int i;

	if (!dev->of_node)
		return -EINVAL;
	printk("++++++++++++xlnx_of_component_probe:%s\n", dev->of_node->name);
	//初始化match對象
	component_match_add(master_dev, &match, compare_of, dev->of_node);

	for (i = 0; ; i++) {
		port = of_parse_phandle(dev->of_node, "ports", i);
		if (!port)
			break;

		parent = port->parent;
		if (!of_node_cmp(parent->name, "ports"))
			parent = parent->parent;
		parent = of_node_get(parent);

		if (!of_device_is_available(parent)) {
			of_node_put(parent);
			of_node_put(port);
			continue;
		}
		printk("++++++++++++xlnx_of_component_probe parent :%s\n", parent->name);
		component_match_add(master_dev, &match, compare_of, parent);
		of_node_put(parent);
		of_node_put(port);
	}

	parent = dev->of_node;
	for (i = 0; ; i++) {
		parent = of_node_get(parent);
		if (!of_device_is_available(parent)) {
			of_node_put(parent);
			continue;
		}
		//remote 節點node remote-endpoint = <&hdmi_encoder>; 父節點:v_hdmi_tx_ss
		//match對象初始化父節點
		for_each_endpoint_of_node(parent, ep) {
			remote = of_graph_get_remote_port_parent(ep);
			if (!remote || !of_device_is_available(remote) ||
			    remote == dev->of_node) {
				of_node_put(remote);
				continue;
			} else if (!of_device_is_available(remote->parent)) {
				dev_warn(dev, "parent dev of %s unavailable\n",
					 remote->full_name);
				of_node_put(remote);
				continue;
			}
			printk("++++++++++++xlnx_of_component_probe remote :%s\n", remote->name);
			component_match_add(master_dev, &match, compare_of,
					    remote);
			of_node_put(remote);
		}
		of_node_put(parent);

		port = of_parse_phandle(dev->of_node, "ports", i);
		if (!port)
			break;

		parent = port->parent;
		if (!of_node_cmp(parent->name, "ports"))
			parent = parent->parent;
		of_node_put(port);
	}
	//找到比對的master,并執行是以bind
	return component_master_add_with_match(master_dev, m_ops, match);
}
           
static int try_to_bring_up_master(struct master *master,
	struct component *component)
{
	int ret;

	dev_dbg(master->dev, "trying to bring up master\n");
	//比對
	if (find_components(master)) {
		dev_dbg(master->dev, "master has incomplete components\n");
		return 0;
	}

	if (component && component->master != master) {
		dev_dbg(master->dev, "master is not for this component (%s)\n",
			dev_name(component->dev));
		return 0;
	}

	if (!devres_open_group(master->dev, NULL, GFP_KERNEL))
		return -ENOMEM;
	//找到了所有元件,則執行xlnx_bind(xlnx_drv.c)
	/* Found all components */
	ret = master->ops->bind(master->dev);
	if (ret < 0) {
		devres_release_group(master->dev, NULL);
		dev_info(master->dev, "master bind failed: %d\n", ret);
		return ret;
	}

	master->bound = true;
	return 1;
}
           
static int find_components(struct master *master)
{
	struct component_match *match = master->match;
	size_t i;
	int ret = 0;

	/*
	 * Scan the array of match functions and attach
	 * any components which are found to this master.
	 */
		//num為2,前面添加兩個match
	for (i = 0; i < match->num; i++) {
		struct component_match_array *mc = &match->compare[i];
		struct component *c;

		dev_dbg(master->dev, "Looking for component %zu\n", i);

		if (match->compare[i].component)
			continue;
		//對比:match的node和已添加component_list的node
		//第一個比對到的是v_drm_dmaengine_drv
		//第二個比對成功的是v_hdmi_tx_ss
		c = find_component(master, mc->compare, mc->data);
		if (!c) {
			ret = -ENXIO;
			break;
		}

		dev_dbg(master->dev, "found component %s, duplicate %u\n", dev_name(c->dev), !!c->master);

		/* Attach this component to the master */
		match->compare[i].duplicate = !!c->master;
		match->compare[i].component = c;
		c->master = master;
	}
	return ret;
}
           

這裡比對的工作大體是:match的node和已添加component_list的node比對:

match:

賽靈思xilinx平台drm分析

component:

賽靈思xilinx平台drm分析
賽靈思xilinx平台drm分析

   比對成功則執行xlnx_bind,即drm所有元件初始化和drm裝置注冊。

static int xlnx_bind(struct device *dev)
{
	struct xlnx_drm *xlnx_drm;
	struct drm_device *drm;
	const struct drm_format_info *info;
	struct platform_device *master = to_platform_device(dev);
	struct platform_device *pdev = to_platform_device(dev->parent);
	int ret;
	u32 format;

	drm = drm_dev_alloc(&xlnx_drm_driver, &pdev->dev);
	if (IS_ERR(drm))
		return PTR_ERR(drm);

	xlnx_drm = devm_kzalloc(drm->dev, sizeof(*xlnx_drm), GFP_KERNEL);
	if (!xlnx_drm) {
		ret = -ENOMEM;
		goto err_drm;
	}

	drm_mode_config_init(drm);
	drm->mode_config.funcs = &xlnx_mode_config_funcs;

	ret = drm_vblank_init(drm, 1);
	if (ret) {
		dev_err(&pdev->dev, "failed to initialize vblank\n");
		goto err_xlnx_drm;
	}

	drm->irq_enabled = 1;
	drm->dev_private = xlnx_drm;
	xlnx_drm->drm = drm;
	xlnx_drm->master = master;
	drm_kms_helper_poll_init(drm);
	platform_set_drvdata(master, xlnx_drm);

	xlnx_drm->crtc = xlnx_crtc_helper_init(drm);
	if (IS_ERR(xlnx_drm->crtc)) {
		ret = PTR_ERR(xlnx_drm->crtc);
		goto err_xlnx_drm;
	}

	ret = component_bind_all(&master->dev, drm);
	if (ret)
		goto err_crtc;

	xlnx_mode_config_init(drm);
	
	drm_mode_config_reset(drm);
	
	dma_set_mask(drm->dev, xlnx_crtc_helper_get_dma_mask(xlnx_drm->crtc));

	format = xlnx_crtc_helper_get_format(xlnx_drm->crtc);

	info = drm_format_info(format);
	
	if (info && info->depth && info->cpp[0]) {
		unsigned int align;

		align = xlnx_crtc_helper_get_align(xlnx_drm->crtc);
		xlnx_drm->fb = xlnx_fb_init(drm, info->cpp[0] * 8, 1, align,
					    xlnx_fbdev_vres);
		if (IS_ERR(xlnx_drm->fb)) {
			dev_err(&pdev->dev,
				"failed to initialize drm fb\n");
			xlnx_drm->fb = NULL;
		}
	} else {
		/* fbdev emulation is optional */
		dev_info(&pdev->dev, "fbdev is not initialized\n");
	}

	ret = drm_dev_register(drm, 0);
	if (ret < 0)
		goto err_fb;
	

	return 0;

err_fb:
	if (xlnx_drm->fb)
		xlnx_fb_fini(xlnx_drm->fb);
	component_unbind_all(drm->dev, drm);
err_crtc:
	xlnx_crtc_helper_fini(drm, xlnx_drm->crtc);
err_xlnx_drm:
	drm_mode_config_cleanup(drm);
err_drm:
	drm_dev_unref(drm);
	return ret;
}
           

繼續閱讀