Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b333ad00 authored by Gjorgji Rosikopulos's avatar Gjorgji Rosikopulos
Browse files

msm: face detect: add shared vbif power collapse sequence



Power collapse sequence is needed if vbif is shared between
face detection and other modules.

In that cases before face detection clocks are disabled
we need to issue stop command and wait core to halt.
Halt irq is received from face detection wrapper and
we need to attach one more irq.

For simplicity of implementation move
irq handling to hw file and keep device
implementation simple.

Power collapse sequence will be executed
only on 0x10010000 or higher revisions of face detection
core.

Change-Id: I2585e80686e2052e8b74a50764f47c81ebcb1815
Signed-off-by: default avatarGjorgji Rosikopulos <grosik@codeaurora.org>
parent 51c75088
Loading
Loading
Loading
Loading
+27 −2
Original line number Diff line number Diff line
@@ -16,7 +16,9 @@ Required properties:
    - "fd_core" - FD CORE hardware register set.
    - "fd_misc" - FD MISC hardware register set.
    - "fd_vbif" - FD VBIF hardware register set.
- interrupts: should contain the fd interrupts.
- interrupts: should contain the fd interrupts. From fd cores with
  revisions 0x10010000 and higher, power collapse sequence is required.
  Face detection wrapper irq is needed to perform power collapse.
- interrupt-names: should specify relevant names to each interrupts
  property defined.
- vdd-supply: phandle to GDSC regulator controlling face detection hw.
@@ -33,7 +35,7 @@ Optional properties:
  If we want to have different operating clock frequencies we can define
  rate levels. They should be defined in incremental order.

Example:
Example revisions 0x10000000..0x10010000:

    qcom,fd@fd878000 {
        compatible = "qcom,face-detection";
@@ -54,3 +56,26 @@ Example:
			<200000000 200000000 150000000 40000000>,
			<400000000 400000000 333000000 80000000>;
    };

Example revision 0x10010000 and above:

    qcom,fd@fd878000 {
        compatible = "qcom,face-detection";
        reg = <0xfd878000 0x800>,
              <0xfd87c000 0x800>,
              <0xfd860000 0x1000>;
        reg-names = "fd_core", "fd_misc", "fd_vbif";
        interrupts = <0 316 0>;
        interrupt-names = "fd";
        vdd-supply = <&gdsc_fd>;
        clocks = <&clock_mmss clk_fd_core_clk>,
	         <&clock_mmss clk_fd_core_uar_clk>,
	         <&clock_mmss clk_fd_axi_clk>,
	         <&clock_mmss clk_fd_ahb_clk>;
        clock-names = "fd_core_clk", "fd_core_uar_clk",
                      "fd_axi_clk", "fd_ahb_clk";
        clock-rates = <60000000 60000000 75000000 40000000>,
                      <200000000 200000000 150000000 40000000>,
                      <400000000 400000000 333000000 80000000>;
   };
+16 −42
Original line number Diff line number Diff line
@@ -1193,23 +1193,6 @@ static void msm_fd_wq_handler(struct work_struct *work)
	msm_fd_hw_buffer_done(fd, active_buf);
}

/*
 * msm_fd_irq - Fd device irq handler.
 * @irq: Pointer to work struct.
 * @dev_id: Pointer to fd device.
 */
static irqreturn_t msm_fd_irq(int irq, void *dev_id)
{
	struct msm_fd_device *fd = dev_id;

	if (msm_fd_hw_is_finished(fd))
		queue_work(fd->work_queue, &fd->work);
	else
		dev_err(fd->dev, "Something wrong! FD still running\n");

	return IRQ_HANDLED;
}

/*
 * fd_probe - Fd device probe method.
 * @pdev: Pointer fd platform device.
@@ -1226,6 +1209,8 @@ static int fd_probe(struct platform_device *pdev)

	mutex_init(&fd->lock);
	spin_lock_init(&fd->slock);
	init_completion(&fd->hw_halt_completion);
	INIT_LIST_HEAD(&fd->buf_queue);
	fd->dev = &pdev->dev;

	/* Get resources */
@@ -1255,29 +1240,21 @@ static int fd_probe(struct platform_device *pdev)
		goto error_iommu_get;
	}

	fd->irq_num = platform_get_irq(pdev, 0);
	if (fd->irq_num < 0) {
		dev_err(&pdev->dev, "Can not get fd irq resource\n");
		ret = -ENODEV;
		goto error_irq_request;
	/* Get face detect hw before read engine revision */
	ret = msm_fd_hw_get(fd, 0);
	if (ret < 0) {
		dev_err(&pdev->dev, "Fail to get hw\n");
		goto error_iommu_get;
	}
	fd->hw_revision = msm_fd_hw_get_revision(fd);

	ret = devm_request_irq(&pdev->dev, fd->irq_num, msm_fd_irq,
		IRQF_TRIGGER_RISING, dev_name(&pdev->dev), fd);
	if (ret) {
		dev_err(&pdev->dev, "Can not claim IRQ %d\n", fd->irq_num);
		goto error_irq_request;
	}
	msm_fd_hw_put(fd);

	fd->work_queue = alloc_workqueue(MSM_FD_DRV_NAME,
		WQ_HIGHPRI | WQ_NON_REENTRANT | WQ_UNBOUND, 0);
	if (!fd->work_queue) {
		dev_err(&pdev->dev, "Can not register workqueue\n");
		ret = -ENOMEM;
		goto error_alloc_workqueue;
	ret = msm_fd_hw_request_irq(pdev, fd, msm_fd_wq_handler);
	if (ret < 0) {
		dev_err(&pdev->dev, "Fail request irq\n");
		goto error_request_irq;
	}
	INIT_WORK(&fd->work, msm_fd_wq_handler);
	INIT_LIST_HEAD(&fd->buf_queue);

	/* v4l2 device */
	ret = v4l2_device_register(&pdev->dev, &fd->v4l2_dev);
@@ -1311,10 +1288,8 @@ static int fd_probe(struct platform_device *pdev)
error_video_register:
	v4l2_device_unregister(&fd->v4l2_dev);
error_v4l2_register:
	destroy_workqueue(fd->work_queue);
error_alloc_workqueue:
	devm_free_irq(&pdev->dev, fd->irq_num, fd);
error_irq_request:
	msm_fd_hw_release_irq(fd);
error_request_irq:
	msm_fd_hw_put_iommu(fd);
error_iommu_get:
	msm_fd_hw_put_clocks(fd);
@@ -1341,9 +1316,8 @@ static int fd_device_remove(struct platform_device *pdev)
		return 0;
	}
	video_unregister_device(&fd->video);
	destroy_workqueue(fd->work_queue);
	v4l2_device_unregister(&fd->v4l2_dev);
	devm_free_irq(&pdev->dev, fd->irq_num, fd);
	msm_fd_hw_release_irq(fd);
	msm_fd_hw_put_iommu(fd);
	msm_fd_hw_put_clocks(fd);
	regulator_put(fd->vdd);
+9 −3
Original line number Diff line number Diff line
@@ -144,7 +144,6 @@ struct msm_fd_stats {
 * @mem_pool: FD hw memory pool.
 * @stats: Pointer to statistic buffers.
 * @work_buf: Working memory buffer handle.
 * @wait_stop_stream: Pointer to completion to wait on stop stream.
 */
struct fd_ctx {
	struct msm_fd_device *fd_device;
@@ -157,7 +156,6 @@ struct fd_ctx {
	struct msm_fd_mem_pool mem_pool;
	struct msm_fd_stats *stats;
	struct msm_fd_buf_handle work_buf;
	struct completion *wait_stop_stream;
};

/*
@@ -187,8 +185,11 @@ enum msm_fd_mem_resources {

/*
 * struct msm_fd_device - FD device structure.
 * @hw_revision: Face detection hw revision.
 * @lock: Lock used for reference count.
 * @slock: Spinlock used to protect FD device struct.
 * @core_irq_num: Face detection core irq number.
 * wrap_irq_num: Face detection wrapper irq number.
 * @ref_count: Device reference count.
 * @res_mem: Array of memory resources used by FD device.
 * @iomem_base: Array of register mappings used by FD device.
@@ -208,13 +209,17 @@ enum msm_fd_mem_resources {
 * @buf_queue: FD device processing queue.
 * @work_queue: Pointer to FD device IRQ bottom half workqueue.
 * @work: IRQ bottom half work struct.
 * @hw_halt_completion: Completes when face detection hw halt completes.
 */
struct msm_fd_device {
	u32 hw_revision;

	struct mutex lock;
	spinlock_t slock;
	int ref_count;

	int irq_num;
	int core_irq_num;
	int wrap_irq_num;
	struct resource *res_mem[MSM_FD_IOMEM_LAST];
	void __iomem *iomem_base[MSM_FD_IOMEM_LAST];
	struct resource *ioarea[MSM_FD_IOMEM_LAST];
@@ -240,6 +245,7 @@ struct msm_fd_device {
	struct list_head buf_queue;
	struct workqueue_struct *work_queue;
	struct work_struct work;
	struct completion hw_halt_completion;
};

#endif /* __MSM_FD_DEV_H__ */
+208 −3
Original line number Diff line number Diff line
@@ -29,8 +29,14 @@
#include "msm_fd_hw.h"
#include "msm_fd_regs.h"

/* After which revision halt for engine is needed */
#define MSM_FD_HALT_FROM_REV 0x10010000
/* Face detection workqueue name */
#define MSM_FD_WORQUEUE_NAME "face-detection"
/* Face detection processing timeout in ms */
#define MSM_FD_PROCESSING_TIMEOUT_MS 500
/* Face detection halt timeout in ms */
#define MSM_FD_HALT_TIMEOUT_MS 100

/* Fd iommu partition definition */
static struct msm_iova_partition msm_fd_fw_partition = {
@@ -292,7 +298,7 @@ static inline void msm_fd_hw_run(struct msm_fd_device *fd)
 *
 * NOTE: If finish bit is not set, we should not read the result.
 */
int msm_fd_hw_is_finished(struct msm_fd_device *fd)
static int msm_fd_hw_is_finished(struct msm_fd_device *fd)
{
	u32 reg;

@@ -305,7 +311,7 @@ int msm_fd_hw_is_finished(struct msm_fd_device *fd)
 * msm_fd_hw_is_runnig - Check if fd hw engine is busy.
 * @fd: Pointer to fd device.
 */
int msm_fd_hw_is_runnig(struct msm_fd_device *fd)
static int msm_fd_hw_is_runnig(struct msm_fd_device *fd)
{
	u32 reg;

@@ -314,6 +320,46 @@ int msm_fd_hw_is_runnig(struct msm_fd_device *fd)
	return reg & MSM_FD_CONTROL_RUN;
}

/*
 * msm_fd_hw_misc_irq_is_halt - Check if fd received misc halt irq.
 * @fd: Pointer to fd device.
 */
static int msm_fd_hw_misc_irq_is_halt(struct msm_fd_device *fd)
{
	u32 reg;

	reg = msm_fd_hw_read_reg(fd, MSM_FD_IOMEM_MISC,
		MSM_FD_MISC_IRQ_STATUS);

	return reg & MSM_FD_MISC_IRQ_STATUS_HALT_REQ;
}

/*
* msm_fd_hw_misc_clear_all_irq - Clear all misc irq statuses.
* @fd: Pointer to fd device.
*/
static void msm_fd_hw_misc_clear_all_irq(struct msm_fd_device *fd)
{
	msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_MISC, MSM_FD_MISC_IRQ_CLEAR,
		MSM_FD_MISC_IRQ_CLEAR_HALT | MSM_FD_MISC_IRQ_CLEAR_CORE);
}

/*
 * msm_fd_hw_get_revision - Get hw revision and store in to device.
 * @fd: Pointer to fd device.
 */
int msm_fd_hw_get_revision(struct msm_fd_device *fd)
{
	u32 reg;

	reg = msm_fd_hw_read_reg(fd, MSM_FD_IOMEM_MISC,
		MSM_FD_MISC_HW_VERSION);

	dev_dbg(fd->dev, "Face detection hw revision 0x%x\n", reg);

	return reg;
}

/*
 * msm_fd_hw_get_result_x - Get fd result center x coordinate.
 * @fd: Pointer to fd device.
@@ -404,13 +450,171 @@ void msm_fd_hw_get_result_angle_pose(struct msm_fd_device *fd, int idx,
	}
}

/*
 * msm_fd_hw_halt_needed - Check if fd core halt is needed.
 * @fd: Pointer to fd device.
 */
static int msm_fd_hw_halt_needed(struct msm_fd_device *fd)
{
	return fd->hw_revision >= MSM_FD_HALT_FROM_REV;
}

/*
 * msm_fd_hw_halt - Halt fd core.
 * @fd: Pointer to fd device.
 */
static void msm_fd_hw_halt(struct msm_fd_device *fd)
{
	unsigned long time;

	if (msm_fd_hw_halt_needed(fd)) {
		init_completion(&fd->hw_halt_completion);

		msm_fd_hw_write_reg(fd, MSM_FD_IOMEM_MISC, MSM_FD_HW_STOP, 1);

		time = wait_for_completion_timeout(&fd->hw_halt_completion,
			msecs_to_jiffies(MSM_FD_HALT_TIMEOUT_MS));
		if (!time)
			dev_err(fd->dev, "Face detection halt timeout\n");

	}
}

/*
 * msm_fd_hw_misc_irq_needed - Check if misc irq is needed
 * @fd: Pointer to fd device.
 */
static int msm_fd_hw_misc_irq_needed(struct msm_fd_device *fd)
{
	return msm_fd_hw_halt_needed(fd);
}

/*
 * msm_fd_core_irq - Face detection core irq handler.
 * @irq: Irq number.
 * @dev_id: Pointer to fd device.
 */
static irqreturn_t msm_fd_hw_core_irq(int irq, void *dev_id)
{
	struct msm_fd_device *fd = dev_id;

	if (msm_fd_hw_is_finished(fd))
		queue_work(fd->work_queue, &fd->work);
	else
		dev_err(fd->dev, "Something wrong! FD still running\n");

	return IRQ_HANDLED;
}

/*
 * msm_fd_wrap_irq - Face detection wrapper irq handler.
 * @irq: Irq number.
 * @dev_id: Pointer to fd device.
 */
static irqreturn_t msm_fd_hw_wrap_irq(int irq, void *dev_id)
{
	struct msm_fd_device *fd = dev_id;

	if (msm_fd_hw_misc_irq_is_halt(fd))
		complete_all(&fd->hw_halt_completion);

	msm_fd_hw_misc_clear_all_irq(fd);

	return IRQ_HANDLED;
}

/*
 * msm_fd_hw_request_irq - Configure and enable vbif interface.
 * @pdev: Pointer to platform device.
 * @fd: Pointer to fd device.
 * @work_func: Pointer to work func used for irq bottom half.
 */
int msm_fd_hw_request_irq(struct platform_device *pdev,
	struct msm_fd_device *fd, work_func_t work_func)
{
	int ret;

	fd->core_irq_num = platform_get_irq(pdev, 0);
	if (fd->core_irq_num < 0) {
		dev_err(fd->dev, "Can not get fd core irq resource\n");
		ret = -ENODEV;
		goto error_core_irq;
	}

	ret = devm_request_irq(fd->dev, fd->core_irq_num, msm_fd_hw_core_irq,
		IRQF_TRIGGER_RISING, dev_name(fd->dev), fd);
	if (ret) {
		dev_err(&pdev->dev, "Can not claim core IRQ %d\n",
			fd->core_irq_num);
		goto error_core_irq;
	}

	/* If vbif is shared we will need wrapper irq for releasing vbif */
	fd->core_irq_num = -1;
	if (msm_fd_hw_misc_irq_needed(fd)) {
		fd->wrap_irq_num = platform_get_irq(pdev, 1);
		if (fd->core_irq_num < 0) {
			dev_err(fd->dev, "Can not get fd wrap irq resource\n");
			ret = -ENODEV;
			goto error_wrap_irq;
		}

		ret = devm_request_irq(fd->dev, fd->wrap_irq_num,
			msm_fd_hw_wrap_irq, IRQF_TRIGGER_RISING,
			dev_name(&pdev->dev), fd);
		if (ret) {
			dev_err(fd->dev, "Can not claim wrapper IRQ %d\n",
				fd->wrap_irq_num);
			goto error_wrap_irq;
		}
	}

	fd->work_queue = alloc_workqueue(MSM_FD_WORQUEUE_NAME,
		WQ_HIGHPRI | WQ_NON_REENTRANT | WQ_UNBOUND, 0);
	if (!fd->work_queue) {
		dev_err(fd->dev, "Can not register workqueue\n");
		ret = -ENOMEM;
		goto error_alloc_workqueue;
	}
	INIT_WORK(&fd->work, work_func);

	return 0;

error_alloc_workqueue:
	if (fd->wrap_irq_num >= 0)
		devm_free_irq(fd->dev, fd->wrap_irq_num, fd);
error_wrap_irq:
	devm_free_irq(&pdev->dev, fd->core_irq_num, fd);
error_core_irq:
	return ret;
}

/*
 * msm_fd_hw_release_irq - Free core and wrap irq.
 * @fd: Pointer to fd device.
 */
void msm_fd_hw_release_irq(struct msm_fd_device *fd)
{
	if (fd->core_irq_num >= 0) {
		devm_free_irq(fd->dev, fd->core_irq_num, fd);
		fd->core_irq_num = -1;
	}
	if (fd->wrap_irq_num >= 0) {
		devm_free_irq(fd->dev, fd->wrap_irq_num, fd);
		fd->wrap_irq_num = -1;
	}
	if (fd->work_queue) {
		destroy_workqueue(fd->work_queue);
		fd->work_queue = NULL;
	}
}

/*
 * msm_fd_hw_vbif_register - Configure and enable vbif interface.
 * @fd: Pointer to fd device.
 */
void msm_fd_hw_vbif_register(struct msm_fd_device *fd)
{

	msm_fd_hw_reg_set(fd, MSM_FD_IOMEM_VBIF,
		MSM_FD_VBIF_CLKON, 0x1);

@@ -854,6 +1058,7 @@ void msm_fd_hw_put(struct msm_fd_device *fd)
	BUG_ON(fd->ref_count == 0);

	if (--fd->ref_count == 0) {
		msm_fd_hw_halt(fd);
		msm_fd_hw_vbif_unregister(fd);
		msm_fd_hw_bus_release(fd);
		msm_fd_hw_disable_clocks(fd);
+7 −2
Original line number Diff line number Diff line
@@ -15,8 +15,6 @@

#include "msm_fd_dev.h"

int msm_fd_hw_is_finished(struct msm_fd_device *fd);

int msm_fd_hw_get_face_count(struct msm_fd_device *fd);

int msm_fd_hw_get_result_x(struct msm_fd_device *fd, int idx);
@@ -29,6 +27,13 @@ void msm_fd_hw_get_result_conf_size(struct msm_fd_device *fd,
void msm_fd_hw_get_result_angle_pose(struct msm_fd_device *fd, int idx,
	u32 *angle, u32 *pose);

int msm_fd_hw_request_irq(struct platform_device *pdev,
	struct msm_fd_device *fd, work_func_t work_func);

void msm_fd_hw_release_irq(struct msm_fd_device *fd);

int msm_fd_hw_get_revision(struct msm_fd_device *fd);

void msm_fd_hw_release_mem_resources(struct msm_fd_device *fd);

int msm_fd_hw_get_mem_resources(struct platform_device *pdev,
Loading