12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622 |
- /*
- * Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
- * Copyright (C) 2017 Linaro Ltd.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
- #include <linux/delay.h>
- #include <linux/device.h>
- #include <linux/dma-mapping.h>
- #include <linux/interrupt.h>
- #include <linux/iopoll.h>
- #include <linux/kernel.h>
- #include <linux/qcom_scm.h>
- #include <linux/slab.h>
- #include "core.h"
- #include "hfi_cmds.h"
- #include "hfi_msgs.h"
- #include "hfi_venus.h"
- #include "hfi_venus_io.h"
- #define HFI_MASK_QHDR_TX_TYPE 0xff000000
- #define HFI_MASK_QHDR_RX_TYPE 0x00ff0000
- #define HFI_MASK_QHDR_PRI_TYPE 0x0000ff00
- #define HFI_MASK_QHDR_ID_TYPE 0x000000ff
- #define HFI_HOST_TO_CTRL_CMD_Q 0
- #define HFI_CTRL_TO_HOST_MSG_Q 1
- #define HFI_CTRL_TO_HOST_DBG_Q 2
- #define HFI_MASK_QHDR_STATUS 0x000000ff
- #define IFACEQ_NUM 3
- #define IFACEQ_CMD_IDX 0
- #define IFACEQ_MSG_IDX 1
- #define IFACEQ_DBG_IDX 2
- #define IFACEQ_MAX_BUF_COUNT 50
- #define IFACEQ_MAX_PARALLEL_CLNTS 16
- #define IFACEQ_DFLT_QHDR 0x01010000
- #define POLL_INTERVAL_US 50
- #define IFACEQ_MAX_PKT_SIZE 1024
- #define IFACEQ_MED_PKT_SIZE 768
- #define IFACEQ_MIN_PKT_SIZE 8
- #define IFACEQ_VAR_SMALL_PKT_SIZE 100
- #define IFACEQ_VAR_LARGE_PKT_SIZE 512
- #define IFACEQ_VAR_HUGE_PKT_SIZE (1024 * 12)
- enum tzbsp_video_state {
- TZBSP_VIDEO_STATE_SUSPEND = 0,
- TZBSP_VIDEO_STATE_RESUME
- };
- struct hfi_queue_table_header {
- u32 version;
- u32 size;
- u32 qhdr0_offset;
- u32 qhdr_size;
- u32 num_q;
- u32 num_active_q;
- };
- struct hfi_queue_header {
- u32 status;
- u32 start_addr;
- u32 type;
- u32 q_size;
- u32 pkt_size;
- u32 pkt_drop_cnt;
- u32 rx_wm;
- u32 tx_wm;
- u32 rx_req;
- u32 tx_req;
- u32 rx_irq_status;
- u32 tx_irq_status;
- u32 read_idx;
- u32 write_idx;
- };
- #define IFACEQ_TABLE_SIZE \
- (sizeof(struct hfi_queue_table_header) + \
- sizeof(struct hfi_queue_header) * IFACEQ_NUM)
- #define IFACEQ_QUEUE_SIZE (IFACEQ_MAX_PKT_SIZE * \
- IFACEQ_MAX_BUF_COUNT * IFACEQ_MAX_PARALLEL_CLNTS)
- #define IFACEQ_GET_QHDR_START_ADDR(ptr, i) \
- (void *)(((ptr) + sizeof(struct hfi_queue_table_header)) + \
- ((i) * sizeof(struct hfi_queue_header)))
- #define QDSS_SIZE SZ_4K
- #define SFR_SIZE SZ_4K
- #define QUEUE_SIZE \
- (IFACEQ_TABLE_SIZE + (IFACEQ_QUEUE_SIZE * IFACEQ_NUM))
- #define ALIGNED_QDSS_SIZE ALIGN(QDSS_SIZE, SZ_4K)
- #define ALIGNED_SFR_SIZE ALIGN(SFR_SIZE, SZ_4K)
- #define ALIGNED_QUEUE_SIZE ALIGN(QUEUE_SIZE, SZ_4K)
- #define SHARED_QSIZE ALIGN(ALIGNED_SFR_SIZE + ALIGNED_QUEUE_SIZE + \
- ALIGNED_QDSS_SIZE, SZ_1M)
- struct mem_desc {
- dma_addr_t da; /* device address */
- void *kva; /* kernel virtual address */
- u32 size;
- unsigned long attrs;
- };
- struct iface_queue {
- struct hfi_queue_header *qhdr;
- struct mem_desc qmem;
- };
- enum venus_state {
- VENUS_STATE_DEINIT = 1,
- VENUS_STATE_INIT,
- };
- struct venus_hfi_device {
- struct venus_core *core;
- u32 irq_status;
- u32 last_packet_type;
- bool power_enabled;
- bool suspended;
- enum venus_state state;
- /* serialize read / write to the shared memory */
- struct mutex lock;
- struct completion pwr_collapse_prep;
- struct completion release_resource;
- struct mem_desc ifaceq_table;
- struct mem_desc sfr;
- struct iface_queue queues[IFACEQ_NUM];
- u8 pkt_buf[IFACEQ_VAR_HUGE_PKT_SIZE];
- u8 dbg_buf[IFACEQ_VAR_HUGE_PKT_SIZE];
- };
- static bool venus_pkt_debug;
- static int venus_fw_debug = HFI_DEBUG_MSG_ERROR | HFI_DEBUG_MSG_FATAL;
- static bool venus_sys_idle_indicator;
- static bool venus_fw_low_power_mode = true;
- static int venus_hw_rsp_timeout = 1000;
- static bool venus_fw_coverage;
- static void venus_set_state(struct venus_hfi_device *hdev,
- enum venus_state state)
- {
- mutex_lock(&hdev->lock);
- hdev->state = state;
- mutex_unlock(&hdev->lock);
- }
- static bool venus_is_valid_state(struct venus_hfi_device *hdev)
- {
- return hdev->state != VENUS_STATE_DEINIT;
- }
- static void venus_dump_packet(struct venus_hfi_device *hdev, const void *packet)
- {
- size_t pkt_size = *(u32 *)packet;
- if (!venus_pkt_debug)
- return;
- print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 16, 1, packet,
- pkt_size, true);
- }
- static int venus_write_queue(struct venus_hfi_device *hdev,
- struct iface_queue *queue,
- void *packet, u32 *rx_req)
- {
- struct hfi_queue_header *qhdr;
- u32 dwords, new_wr_idx;
- u32 empty_space, rd_idx, wr_idx, qsize;
- u32 *wr_ptr;
- if (!queue->qmem.kva)
- return -EINVAL;
- qhdr = queue->qhdr;
- if (!qhdr)
- return -EINVAL;
- venus_dump_packet(hdev, packet);
- dwords = (*(u32 *)packet) >> 2;
- if (!dwords)
- return -EINVAL;
- rd_idx = qhdr->read_idx;
- wr_idx = qhdr->write_idx;
- qsize = qhdr->q_size;
- /* ensure rd/wr indices's are read from memory */
- rmb();
- if (wr_idx >= rd_idx)
- empty_space = qsize - (wr_idx - rd_idx);
- else
- empty_space = rd_idx - wr_idx;
- if (empty_space <= dwords) {
- qhdr->tx_req = 1;
- /* ensure tx_req is updated in memory */
- wmb();
- return -ENOSPC;
- }
- qhdr->tx_req = 0;
- /* ensure tx_req is updated in memory */
- wmb();
- new_wr_idx = wr_idx + dwords;
- wr_ptr = (u32 *)(queue->qmem.kva + (wr_idx << 2));
- if (new_wr_idx < qsize) {
- memcpy(wr_ptr, packet, dwords << 2);
- } else {
- size_t len;
- new_wr_idx -= qsize;
- len = (dwords - new_wr_idx) << 2;
- memcpy(wr_ptr, packet, len);
- memcpy(queue->qmem.kva, packet + len, new_wr_idx << 2);
- }
- /* make sure packet is written before updating the write index */
- wmb();
- qhdr->write_idx = new_wr_idx;
- *rx_req = qhdr->rx_req ? 1 : 0;
- /* make sure write index is updated before an interrupt is raised */
- mb();
- return 0;
- }
- static int venus_read_queue(struct venus_hfi_device *hdev,
- struct iface_queue *queue, void *pkt, u32 *tx_req)
- {
- struct hfi_queue_header *qhdr;
- u32 dwords, new_rd_idx;
- u32 rd_idx, wr_idx, type, qsize;
- u32 *rd_ptr;
- u32 recv_request = 0;
- int ret = 0;
- if (!queue->qmem.kva)
- return -EINVAL;
- qhdr = queue->qhdr;
- if (!qhdr)
- return -EINVAL;
- type = qhdr->type;
- rd_idx = qhdr->read_idx;
- wr_idx = qhdr->write_idx;
- qsize = qhdr->q_size;
- /* make sure data is valid before using it */
- rmb();
- /*
- * Do not set receive request for debug queue, if set, Venus generates
- * interrupt for debug messages even when there is no response message
- * available. In general debug queue will not become full as it is being
- * emptied out for every interrupt from Venus. Venus will anyway
- * generates interrupt if it is full.
- */
- if (type & HFI_CTRL_TO_HOST_MSG_Q)
- recv_request = 1;
- if (rd_idx == wr_idx) {
- qhdr->rx_req = recv_request;
- *tx_req = 0;
- /* update rx_req field in memory */
- wmb();
- return -ENODATA;
- }
- rd_ptr = (u32 *)(queue->qmem.kva + (rd_idx << 2));
- dwords = *rd_ptr >> 2;
- if (!dwords)
- return -EINVAL;
- new_rd_idx = rd_idx + dwords;
- if (((dwords << 2) <= IFACEQ_VAR_HUGE_PKT_SIZE) && rd_idx <= qsize) {
- if (new_rd_idx < qsize) {
- memcpy(pkt, rd_ptr, dwords << 2);
- } else {
- size_t len;
- new_rd_idx -= qsize;
- len = (dwords - new_rd_idx) << 2;
- memcpy(pkt, rd_ptr, len);
- memcpy(pkt + len, queue->qmem.kva, new_rd_idx << 2);
- }
- } else {
- /* bad packet received, dropping */
- new_rd_idx = qhdr->write_idx;
- ret = -EBADMSG;
- }
- /* ensure the packet is read before updating read index */
- rmb();
- qhdr->read_idx = new_rd_idx;
- /* ensure updating read index */
- wmb();
- rd_idx = qhdr->read_idx;
- wr_idx = qhdr->write_idx;
- /* ensure rd/wr indices are read from memory */
- rmb();
- if (rd_idx != wr_idx)
- qhdr->rx_req = 0;
- else
- qhdr->rx_req = recv_request;
- *tx_req = qhdr->tx_req ? 1 : 0;
- /* ensure rx_req is stored to memory and tx_req is loaded from memory */
- mb();
- venus_dump_packet(hdev, pkt);
- return ret;
- }
- static int venus_alloc(struct venus_hfi_device *hdev, struct mem_desc *desc,
- u32 size)
- {
- struct device *dev = hdev->core->dev;
- desc->attrs = DMA_ATTR_WRITE_COMBINE;
- desc->size = ALIGN(size, SZ_4K);
- desc->kva = dma_alloc_attrs(dev, desc->size, &desc->da, GFP_KERNEL,
- desc->attrs);
- if (!desc->kva)
- return -ENOMEM;
- return 0;
- }
- static void venus_free(struct venus_hfi_device *hdev, struct mem_desc *mem)
- {
- struct device *dev = hdev->core->dev;
- dma_free_attrs(dev, mem->size, mem->kva, mem->da, mem->attrs);
- }
- static void venus_writel(struct venus_hfi_device *hdev, u32 reg, u32 value)
- {
- writel(value, hdev->core->base + reg);
- }
- static u32 venus_readl(struct venus_hfi_device *hdev, u32 reg)
- {
- return readl(hdev->core->base + reg);
- }
- static void venus_set_registers(struct venus_hfi_device *hdev)
- {
- const struct venus_resources *res = hdev->core->res;
- const struct reg_val *tbl = res->reg_tbl;
- unsigned int count = res->reg_tbl_size;
- unsigned int i;
- for (i = 0; i < count; i++)
- venus_writel(hdev, tbl[i].reg, tbl[i].value);
- }
- static void venus_soft_int(struct venus_hfi_device *hdev)
- {
- venus_writel(hdev, CPU_IC_SOFTINT, BIT(CPU_IC_SOFTINT_H2A_SHIFT));
- }
- static int venus_iface_cmdq_write_nolock(struct venus_hfi_device *hdev,
- void *pkt)
- {
- struct device *dev = hdev->core->dev;
- struct hfi_pkt_hdr *cmd_packet;
- struct iface_queue *queue;
- u32 rx_req;
- int ret;
- if (!venus_is_valid_state(hdev))
- return -EINVAL;
- cmd_packet = (struct hfi_pkt_hdr *)pkt;
- hdev->last_packet_type = cmd_packet->pkt_type;
- queue = &hdev->queues[IFACEQ_CMD_IDX];
- ret = venus_write_queue(hdev, queue, pkt, &rx_req);
- if (ret) {
- dev_err(dev, "write to iface cmd queue failed (%d)\n", ret);
- return ret;
- }
- if (rx_req)
- venus_soft_int(hdev);
- return 0;
- }
- static int venus_iface_cmdq_write(struct venus_hfi_device *hdev, void *pkt)
- {
- int ret;
- mutex_lock(&hdev->lock);
- ret = venus_iface_cmdq_write_nolock(hdev, pkt);
- mutex_unlock(&hdev->lock);
- return ret;
- }
- static int venus_hfi_core_set_resource(struct venus_core *core, u32 id,
- u32 size, u32 addr, void *cookie)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(core);
- struct hfi_sys_set_resource_pkt *pkt;
- u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE];
- int ret;
- if (id == VIDC_RESOURCE_NONE)
- return 0;
- pkt = (struct hfi_sys_set_resource_pkt *)packet;
- ret = pkt_sys_set_resource(pkt, id, size, addr, cookie);
- if (ret)
- return ret;
- ret = venus_iface_cmdq_write(hdev, pkt);
- if (ret)
- return ret;
- return 0;
- }
- static int venus_boot_core(struct venus_hfi_device *hdev)
- {
- struct device *dev = hdev->core->dev;
- static const unsigned int max_tries = 100;
- u32 ctrl_status = 0;
- unsigned int count = 0;
- int ret = 0;
- venus_writel(hdev, VIDC_CTRL_INIT, BIT(VIDC_CTRL_INIT_CTRL_SHIFT));
- venus_writel(hdev, WRAPPER_INTR_MASK, WRAPPER_INTR_MASK_A2HVCODEC_MASK);
- venus_writel(hdev, CPU_CS_SCIACMDARG3, 1);
- while (!ctrl_status && count < max_tries) {
- ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0);
- if ((ctrl_status & CPU_CS_SCIACMDARG0_ERROR_STATUS_MASK) == 4) {
- dev_err(dev, "invalid setting for UC_REGION\n");
- ret = -EINVAL;
- break;
- }
- usleep_range(500, 1000);
- count++;
- }
- if (count >= max_tries)
- ret = -ETIMEDOUT;
- return ret;
- }
- static u32 venus_hwversion(struct venus_hfi_device *hdev)
- {
- struct device *dev = hdev->core->dev;
- u32 ver = venus_readl(hdev, WRAPPER_HW_VERSION);
- u32 major, minor, step;
- major = ver & WRAPPER_HW_VERSION_MAJOR_VERSION_MASK;
- major = major >> WRAPPER_HW_VERSION_MAJOR_VERSION_SHIFT;
- minor = ver & WRAPPER_HW_VERSION_MINOR_VERSION_MASK;
- minor = minor >> WRAPPER_HW_VERSION_MINOR_VERSION_SHIFT;
- step = ver & WRAPPER_HW_VERSION_STEP_VERSION_MASK;
- dev_dbg(dev, "venus hw version %x.%x.%x\n", major, minor, step);
- return major;
- }
- static int venus_run(struct venus_hfi_device *hdev)
- {
- struct device *dev = hdev->core->dev;
- int ret;
- /*
- * Re-program all of the registers that get reset as a result of
- * regulator_disable() and _enable()
- */
- venus_set_registers(hdev);
- venus_writel(hdev, UC_REGION_ADDR, hdev->ifaceq_table.da);
- venus_writel(hdev, UC_REGION_SIZE, SHARED_QSIZE);
- venus_writel(hdev, CPU_CS_SCIACMDARG2, hdev->ifaceq_table.da);
- venus_writel(hdev, CPU_CS_SCIACMDARG1, 0x01);
- if (hdev->sfr.da)
- venus_writel(hdev, SFR_ADDR, hdev->sfr.da);
- ret = venus_boot_core(hdev);
- if (ret) {
- dev_err(dev, "failed to reset venus core\n");
- return ret;
- }
- venus_hwversion(hdev);
- return 0;
- }
- static int venus_halt_axi(struct venus_hfi_device *hdev)
- {
- void __iomem *base = hdev->core->base;
- struct device *dev = hdev->core->dev;
- u32 val;
- int ret;
- if (IS_V4(hdev->core)) {
- val = venus_readl(hdev, WRAPPER_CPU_AXI_HALT);
- val |= WRAPPER_CPU_AXI_HALT_HALT;
- venus_writel(hdev, WRAPPER_CPU_AXI_HALT, val);
- ret = readl_poll_timeout(base + WRAPPER_CPU_AXI_HALT_STATUS,
- val,
- val & WRAPPER_CPU_AXI_HALT_STATUS_IDLE,
- POLL_INTERVAL_US,
- VBIF_AXI_HALT_ACK_TIMEOUT_US);
- if (ret) {
- dev_err(dev, "AXI bus port halt timeout\n");
- return ret;
- }
- return 0;
- }
- /* Halt AXI and AXI IMEM VBIF Access */
- val = venus_readl(hdev, VBIF_AXI_HALT_CTRL0);
- val |= VBIF_AXI_HALT_CTRL0_HALT_REQ;
- venus_writel(hdev, VBIF_AXI_HALT_CTRL0, val);
- /* Request for AXI bus port halt */
- ret = readl_poll_timeout(base + VBIF_AXI_HALT_CTRL1, val,
- val & VBIF_AXI_HALT_CTRL1_HALT_ACK,
- POLL_INTERVAL_US,
- VBIF_AXI_HALT_ACK_TIMEOUT_US);
- if (ret) {
- dev_err(dev, "AXI bus port halt timeout\n");
- return ret;
- }
- return 0;
- }
- static int venus_power_off(struct venus_hfi_device *hdev)
- {
- int ret;
- if (!hdev->power_enabled)
- return 0;
- ret = qcom_scm_set_remote_state(TZBSP_VIDEO_STATE_SUSPEND, 0);
- if (ret)
- return ret;
- ret = venus_halt_axi(hdev);
- if (ret)
- return ret;
- hdev->power_enabled = false;
- return 0;
- }
- static int venus_power_on(struct venus_hfi_device *hdev)
- {
- int ret;
- if (hdev->power_enabled)
- return 0;
- ret = qcom_scm_set_remote_state(TZBSP_VIDEO_STATE_RESUME, 0);
- if (ret)
- goto err;
- ret = venus_run(hdev);
- if (ret)
- goto err_suspend;
- hdev->power_enabled = true;
- return 0;
- err_suspend:
- qcom_scm_set_remote_state(TZBSP_VIDEO_STATE_SUSPEND, 0);
- err:
- hdev->power_enabled = false;
- return ret;
- }
- static int venus_iface_msgq_read_nolock(struct venus_hfi_device *hdev,
- void *pkt)
- {
- struct iface_queue *queue;
- u32 tx_req;
- int ret;
- if (!venus_is_valid_state(hdev))
- return -EINVAL;
- queue = &hdev->queues[IFACEQ_MSG_IDX];
- ret = venus_read_queue(hdev, queue, pkt, &tx_req);
- if (ret)
- return ret;
- if (tx_req)
- venus_soft_int(hdev);
- return 0;
- }
- static int venus_iface_msgq_read(struct venus_hfi_device *hdev, void *pkt)
- {
- int ret;
- mutex_lock(&hdev->lock);
- ret = venus_iface_msgq_read_nolock(hdev, pkt);
- mutex_unlock(&hdev->lock);
- return ret;
- }
- static int venus_iface_dbgq_read_nolock(struct venus_hfi_device *hdev,
- void *pkt)
- {
- struct iface_queue *queue;
- u32 tx_req;
- int ret;
- ret = venus_is_valid_state(hdev);
- if (!ret)
- return -EINVAL;
- queue = &hdev->queues[IFACEQ_DBG_IDX];
- ret = venus_read_queue(hdev, queue, pkt, &tx_req);
- if (ret)
- return ret;
- if (tx_req)
- venus_soft_int(hdev);
- return 0;
- }
- static int venus_iface_dbgq_read(struct venus_hfi_device *hdev, void *pkt)
- {
- int ret;
- if (!pkt)
- return -EINVAL;
- mutex_lock(&hdev->lock);
- ret = venus_iface_dbgq_read_nolock(hdev, pkt);
- mutex_unlock(&hdev->lock);
- return ret;
- }
- static void venus_set_qhdr_defaults(struct hfi_queue_header *qhdr)
- {
- qhdr->status = 1;
- qhdr->type = IFACEQ_DFLT_QHDR;
- qhdr->q_size = IFACEQ_QUEUE_SIZE / 4;
- qhdr->pkt_size = 0;
- qhdr->rx_wm = 1;
- qhdr->tx_wm = 1;
- qhdr->rx_req = 1;
- qhdr->tx_req = 0;
- qhdr->rx_irq_status = 0;
- qhdr->tx_irq_status = 0;
- qhdr->read_idx = 0;
- qhdr->write_idx = 0;
- }
- static void venus_interface_queues_release(struct venus_hfi_device *hdev)
- {
- mutex_lock(&hdev->lock);
- venus_free(hdev, &hdev->ifaceq_table);
- venus_free(hdev, &hdev->sfr);
- memset(hdev->queues, 0, sizeof(hdev->queues));
- memset(&hdev->ifaceq_table, 0, sizeof(hdev->ifaceq_table));
- memset(&hdev->sfr, 0, sizeof(hdev->sfr));
- mutex_unlock(&hdev->lock);
- }
- static int venus_interface_queues_init(struct venus_hfi_device *hdev)
- {
- struct hfi_queue_table_header *tbl_hdr;
- struct iface_queue *queue;
- struct hfi_sfr *sfr;
- struct mem_desc desc = {0};
- unsigned int offset;
- unsigned int i;
- int ret;
- ret = venus_alloc(hdev, &desc, ALIGNED_QUEUE_SIZE);
- if (ret)
- return ret;
- hdev->ifaceq_table = desc;
- offset = IFACEQ_TABLE_SIZE;
- for (i = 0; i < IFACEQ_NUM; i++) {
- queue = &hdev->queues[i];
- queue->qmem.da = desc.da + offset;
- queue->qmem.kva = desc.kva + offset;
- queue->qmem.size = IFACEQ_QUEUE_SIZE;
- offset += queue->qmem.size;
- queue->qhdr =
- IFACEQ_GET_QHDR_START_ADDR(hdev->ifaceq_table.kva, i);
- venus_set_qhdr_defaults(queue->qhdr);
- queue->qhdr->start_addr = queue->qmem.da;
- if (i == IFACEQ_CMD_IDX)
- queue->qhdr->type |= HFI_HOST_TO_CTRL_CMD_Q;
- else if (i == IFACEQ_MSG_IDX)
- queue->qhdr->type |= HFI_CTRL_TO_HOST_MSG_Q;
- else if (i == IFACEQ_DBG_IDX)
- queue->qhdr->type |= HFI_CTRL_TO_HOST_DBG_Q;
- }
- tbl_hdr = hdev->ifaceq_table.kva;
- tbl_hdr->version = 0;
- tbl_hdr->size = IFACEQ_TABLE_SIZE;
- tbl_hdr->qhdr0_offset = sizeof(struct hfi_queue_table_header);
- tbl_hdr->qhdr_size = sizeof(struct hfi_queue_header);
- tbl_hdr->num_q = IFACEQ_NUM;
- tbl_hdr->num_active_q = IFACEQ_NUM;
- /*
- * Set receive request to zero on debug queue as there is no
- * need of interrupt from video hardware for debug messages
- */
- queue = &hdev->queues[IFACEQ_DBG_IDX];
- queue->qhdr->rx_req = 0;
- ret = venus_alloc(hdev, &desc, ALIGNED_SFR_SIZE);
- if (ret) {
- hdev->sfr.da = 0;
- } else {
- hdev->sfr = desc;
- sfr = hdev->sfr.kva;
- sfr->buf_size = ALIGNED_SFR_SIZE;
- }
- /* ensure table and queue header structs are settled in memory */
- wmb();
- return 0;
- }
- static int venus_sys_set_debug(struct venus_hfi_device *hdev, u32 debug)
- {
- struct hfi_sys_set_property_pkt *pkt;
- u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE];
- int ret;
- pkt = (struct hfi_sys_set_property_pkt *)packet;
- pkt_sys_debug_config(pkt, HFI_DEBUG_MODE_QUEUE, debug);
- ret = venus_iface_cmdq_write(hdev, pkt);
- if (ret)
- return ret;
- return 0;
- }
- static int venus_sys_set_coverage(struct venus_hfi_device *hdev, u32 mode)
- {
- struct hfi_sys_set_property_pkt *pkt;
- u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE];
- int ret;
- pkt = (struct hfi_sys_set_property_pkt *)packet;
- pkt_sys_coverage_config(pkt, mode);
- ret = venus_iface_cmdq_write(hdev, pkt);
- if (ret)
- return ret;
- return 0;
- }
- static int venus_sys_set_idle_message(struct venus_hfi_device *hdev,
- bool enable)
- {
- struct hfi_sys_set_property_pkt *pkt;
- u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE];
- int ret;
- if (!enable)
- return 0;
- pkt = (struct hfi_sys_set_property_pkt *)packet;
- pkt_sys_idle_indicator(pkt, enable);
- ret = venus_iface_cmdq_write(hdev, pkt);
- if (ret)
- return ret;
- return 0;
- }
- static int venus_sys_set_power_control(struct venus_hfi_device *hdev,
- bool enable)
- {
- struct hfi_sys_set_property_pkt *pkt;
- u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE];
- int ret;
- pkt = (struct hfi_sys_set_property_pkt *)packet;
- pkt_sys_power_control(pkt, enable);
- ret = venus_iface_cmdq_write(hdev, pkt);
- if (ret)
- return ret;
- return 0;
- }
- static int venus_get_queue_size(struct venus_hfi_device *hdev,
- unsigned int index)
- {
- struct hfi_queue_header *qhdr;
- if (index >= IFACEQ_NUM)
- return -EINVAL;
- qhdr = hdev->queues[index].qhdr;
- if (!qhdr)
- return -EINVAL;
- return abs(qhdr->read_idx - qhdr->write_idx);
- }
- static int venus_sys_set_default_properties(struct venus_hfi_device *hdev)
- {
- struct device *dev = hdev->core->dev;
- int ret;
- ret = venus_sys_set_debug(hdev, venus_fw_debug);
- if (ret)
- dev_warn(dev, "setting fw debug msg ON failed (%d)\n", ret);
- /*
- * Idle indicator is disabled by default on some 4xx firmware versions,
- * enable it explicitly in order to make suspend functional by checking
- * WFI (wait-for-interrupt) bit.
- */
- if (IS_V4(hdev->core))
- venus_sys_idle_indicator = true;
- ret = venus_sys_set_idle_message(hdev, venus_sys_idle_indicator);
- if (ret)
- dev_warn(dev, "setting idle response ON failed (%d)\n", ret);
- ret = venus_sys_set_power_control(hdev, venus_fw_low_power_mode);
- if (ret)
- dev_warn(dev, "setting hw power collapse ON failed (%d)\n",
- ret);
- return ret;
- }
- static int venus_session_cmd(struct venus_inst *inst, u32 pkt_type)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
- struct hfi_session_pkt pkt;
- pkt_session_cmd(&pkt, pkt_type, inst);
- return venus_iface_cmdq_write(hdev, &pkt);
- }
- static void venus_flush_debug_queue(struct venus_hfi_device *hdev)
- {
- struct device *dev = hdev->core->dev;
- void *packet = hdev->dbg_buf;
- while (!venus_iface_dbgq_read(hdev, packet)) {
- struct hfi_msg_sys_coverage_pkt *pkt = packet;
- if (pkt->hdr.pkt_type != HFI_MSG_SYS_COV) {
- struct hfi_msg_sys_debug_pkt *pkt = packet;
- dev_dbg(dev, "%s", pkt->msg_data);
- }
- }
- }
- static int venus_prepare_power_collapse(struct venus_hfi_device *hdev,
- bool wait)
- {
- unsigned long timeout = msecs_to_jiffies(venus_hw_rsp_timeout);
- struct hfi_sys_pc_prep_pkt pkt;
- int ret;
- init_completion(&hdev->pwr_collapse_prep);
- pkt_sys_pc_prep(&pkt);
- ret = venus_iface_cmdq_write(hdev, &pkt);
- if (ret)
- return ret;
- if (!wait)
- return 0;
- ret = wait_for_completion_timeout(&hdev->pwr_collapse_prep, timeout);
- if (!ret) {
- venus_flush_debug_queue(hdev);
- return -ETIMEDOUT;
- }
- return 0;
- }
- static int venus_are_queues_empty(struct venus_hfi_device *hdev)
- {
- int ret1, ret2;
- ret1 = venus_get_queue_size(hdev, IFACEQ_MSG_IDX);
- if (ret1 < 0)
- return ret1;
- ret2 = venus_get_queue_size(hdev, IFACEQ_CMD_IDX);
- if (ret2 < 0)
- return ret2;
- if (!ret1 && !ret2)
- return 1;
- return 0;
- }
- static void venus_sfr_print(struct venus_hfi_device *hdev)
- {
- struct device *dev = hdev->core->dev;
- struct hfi_sfr *sfr = hdev->sfr.kva;
- void *p;
- if (!sfr)
- return;
- p = memchr(sfr->data, '\0', sfr->buf_size);
- /*
- * SFR isn't guaranteed to be NULL terminated since SYS_ERROR indicates
- * that Venus is in the process of crashing.
- */
- if (!p)
- sfr->data[sfr->buf_size - 1] = '\0';
- dev_err_ratelimited(dev, "SFR message from FW: %s\n", sfr->data);
- }
- static void venus_process_msg_sys_error(struct venus_hfi_device *hdev,
- void *packet)
- {
- struct hfi_msg_event_notify_pkt *event_pkt = packet;
- if (event_pkt->event_id != HFI_EVENT_SYS_ERROR)
- return;
- venus_set_state(hdev, VENUS_STATE_DEINIT);
- /*
- * Once SYS_ERROR received from HW, it is safe to halt the AXI.
- * With SYS_ERROR, Venus FW may have crashed and HW might be
- * active and causing unnecessary transactions. Hence it is
- * safe to stop all AXI transactions from venus subsystem.
- */
- venus_halt_axi(hdev);
- venus_sfr_print(hdev);
- }
- static irqreturn_t venus_isr_thread(struct venus_core *core)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(core);
- const struct venus_resources *res;
- void *pkt;
- u32 msg_ret;
- if (!hdev)
- return IRQ_NONE;
- res = hdev->core->res;
- pkt = hdev->pkt_buf;
- if (hdev->irq_status & WRAPPER_INTR_STATUS_A2HWD_MASK) {
- venus_sfr_print(hdev);
- hfi_process_watchdog_timeout(core);
- }
- while (!venus_iface_msgq_read(hdev, pkt)) {
- msg_ret = hfi_process_msg_packet(core, pkt);
- switch (msg_ret) {
- case HFI_MSG_EVENT_NOTIFY:
- venus_process_msg_sys_error(hdev, pkt);
- break;
- case HFI_MSG_SYS_INIT:
- venus_hfi_core_set_resource(core, res->vmem_id,
- res->vmem_size,
- res->vmem_addr,
- hdev);
- break;
- case HFI_MSG_SYS_RELEASE_RESOURCE:
- complete(&hdev->release_resource);
- break;
- case HFI_MSG_SYS_PC_PREP:
- complete(&hdev->pwr_collapse_prep);
- break;
- default:
- break;
- }
- }
- venus_flush_debug_queue(hdev);
- return IRQ_HANDLED;
- }
- static irqreturn_t venus_isr(struct venus_core *core)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(core);
- u32 status;
- if (!hdev)
- return IRQ_NONE;
- status = venus_readl(hdev, WRAPPER_INTR_STATUS);
- if (status & WRAPPER_INTR_STATUS_A2H_MASK ||
- status & WRAPPER_INTR_STATUS_A2HWD_MASK ||
- status & CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK)
- hdev->irq_status = status;
- venus_writel(hdev, CPU_CS_A2HSOFTINTCLR, 1);
- venus_writel(hdev, WRAPPER_INTR_CLEAR, status);
- return IRQ_WAKE_THREAD;
- }
- static int venus_core_init(struct venus_core *core)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(core);
- struct device *dev = core->dev;
- struct hfi_sys_get_property_pkt version_pkt;
- struct hfi_sys_init_pkt pkt;
- int ret;
- pkt_sys_init(&pkt, HFI_VIDEO_ARCH_OX);
- venus_set_state(hdev, VENUS_STATE_INIT);
- ret = venus_iface_cmdq_write(hdev, &pkt);
- if (ret)
- return ret;
- pkt_sys_image_version(&version_pkt);
- ret = venus_iface_cmdq_write(hdev, &version_pkt);
- if (ret)
- dev_warn(dev, "failed to send image version pkt to fw\n");
- ret = venus_sys_set_default_properties(hdev);
- if (ret)
- return ret;
- return 0;
- }
- static int venus_core_deinit(struct venus_core *core)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(core);
- venus_set_state(hdev, VENUS_STATE_DEINIT);
- hdev->suspended = true;
- hdev->power_enabled = false;
- return 0;
- }
- static int venus_core_ping(struct venus_core *core, u32 cookie)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(core);
- struct hfi_sys_ping_pkt pkt;
- pkt_sys_ping(&pkt, cookie);
- return venus_iface_cmdq_write(hdev, &pkt);
- }
- static int venus_core_trigger_ssr(struct venus_core *core, u32 trigger_type)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(core);
- struct hfi_sys_test_ssr_pkt pkt;
- int ret;
- ret = pkt_sys_ssr_cmd(&pkt, trigger_type);
- if (ret)
- return ret;
- return venus_iface_cmdq_write(hdev, &pkt);
- }
- static int venus_session_init(struct venus_inst *inst, u32 session_type,
- u32 codec)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
- struct hfi_session_init_pkt pkt;
- int ret;
- ret = pkt_session_init(&pkt, inst, session_type, codec);
- if (ret)
- goto err;
- ret = venus_iface_cmdq_write(hdev, &pkt);
- if (ret)
- goto err;
- return 0;
- err:
- venus_flush_debug_queue(hdev);
- return ret;
- }
- static int venus_session_end(struct venus_inst *inst)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
- struct device *dev = hdev->core->dev;
- if (venus_fw_coverage) {
- if (venus_sys_set_coverage(hdev, venus_fw_coverage))
- dev_warn(dev, "fw coverage msg ON failed\n");
- }
- return venus_session_cmd(inst, HFI_CMD_SYS_SESSION_END);
- }
- static int venus_session_abort(struct venus_inst *inst)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
- venus_flush_debug_queue(hdev);
- return venus_session_cmd(inst, HFI_CMD_SYS_SESSION_ABORT);
- }
- static int venus_session_flush(struct venus_inst *inst, u32 flush_mode)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
- struct hfi_session_flush_pkt pkt;
- int ret;
- ret = pkt_session_flush(&pkt, inst, flush_mode);
- if (ret)
- return ret;
- return venus_iface_cmdq_write(hdev, &pkt);
- }
- static int venus_session_start(struct venus_inst *inst)
- {
- return venus_session_cmd(inst, HFI_CMD_SESSION_START);
- }
- static int venus_session_stop(struct venus_inst *inst)
- {
- return venus_session_cmd(inst, HFI_CMD_SESSION_STOP);
- }
- static int venus_session_continue(struct venus_inst *inst)
- {
- return venus_session_cmd(inst, HFI_CMD_SESSION_CONTINUE);
- }
- static int venus_session_etb(struct venus_inst *inst,
- struct hfi_frame_data *in_frame)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
- u32 session_type = inst->session_type;
- int ret;
- if (session_type == VIDC_SESSION_TYPE_DEC) {
- struct hfi_session_empty_buffer_compressed_pkt pkt;
- ret = pkt_session_etb_decoder(&pkt, inst, in_frame);
- if (ret)
- return ret;
- ret = venus_iface_cmdq_write(hdev, &pkt);
- } else if (session_type == VIDC_SESSION_TYPE_ENC) {
- struct hfi_session_empty_buffer_uncompressed_plane0_pkt pkt;
- ret = pkt_session_etb_encoder(&pkt, inst, in_frame);
- if (ret)
- return ret;
- ret = venus_iface_cmdq_write(hdev, &pkt);
- } else {
- ret = -EINVAL;
- }
- return ret;
- }
- static int venus_session_ftb(struct venus_inst *inst,
- struct hfi_frame_data *out_frame)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
- struct hfi_session_fill_buffer_pkt pkt;
- int ret;
- ret = pkt_session_ftb(&pkt, inst, out_frame);
- if (ret)
- return ret;
- return venus_iface_cmdq_write(hdev, &pkt);
- }
- static int venus_session_set_buffers(struct venus_inst *inst,
- struct hfi_buffer_desc *bd)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
- struct hfi_session_set_buffers_pkt *pkt;
- u8 packet[IFACEQ_VAR_LARGE_PKT_SIZE];
- int ret;
- if (bd->buffer_type == HFI_BUFFER_INPUT)
- return 0;
- pkt = (struct hfi_session_set_buffers_pkt *)packet;
- ret = pkt_session_set_buffers(pkt, inst, bd);
- if (ret)
- return ret;
- return venus_iface_cmdq_write(hdev, pkt);
- }
- static int venus_session_unset_buffers(struct venus_inst *inst,
- struct hfi_buffer_desc *bd)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
- struct hfi_session_release_buffer_pkt *pkt;
- u8 packet[IFACEQ_VAR_LARGE_PKT_SIZE];
- int ret;
- if (bd->buffer_type == HFI_BUFFER_INPUT)
- return 0;
- pkt = (struct hfi_session_release_buffer_pkt *)packet;
- ret = pkt_session_unset_buffers(pkt, inst, bd);
- if (ret)
- return ret;
- return venus_iface_cmdq_write(hdev, pkt);
- }
- static int venus_session_load_res(struct venus_inst *inst)
- {
- return venus_session_cmd(inst, HFI_CMD_SESSION_LOAD_RESOURCES);
- }
- static int venus_session_release_res(struct venus_inst *inst)
- {
- return venus_session_cmd(inst, HFI_CMD_SESSION_RELEASE_RESOURCES);
- }
- static int venus_session_parse_seq_hdr(struct venus_inst *inst, u32 seq_hdr,
- u32 seq_hdr_len)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
- struct hfi_session_parse_sequence_header_pkt *pkt;
- u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE];
- int ret;
- pkt = (struct hfi_session_parse_sequence_header_pkt *)packet;
- ret = pkt_session_parse_seq_header(pkt, inst, seq_hdr, seq_hdr_len);
- if (ret)
- return ret;
- ret = venus_iface_cmdq_write(hdev, pkt);
- if (ret)
- return ret;
- return 0;
- }
- static int venus_session_get_seq_hdr(struct venus_inst *inst, u32 seq_hdr,
- u32 seq_hdr_len)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
- struct hfi_session_get_sequence_header_pkt *pkt;
- u8 packet[IFACEQ_VAR_SMALL_PKT_SIZE];
- int ret;
- pkt = (struct hfi_session_get_sequence_header_pkt *)packet;
- ret = pkt_session_get_seq_hdr(pkt, inst, seq_hdr, seq_hdr_len);
- if (ret)
- return ret;
- return venus_iface_cmdq_write(hdev, pkt);
- }
- static int venus_session_set_property(struct venus_inst *inst, u32 ptype,
- void *pdata)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
- struct hfi_session_set_property_pkt *pkt;
- u8 packet[IFACEQ_VAR_LARGE_PKT_SIZE];
- int ret;
- pkt = (struct hfi_session_set_property_pkt *)packet;
- ret = pkt_session_set_property(pkt, inst, ptype, pdata);
- if (ret)
- return ret;
- return venus_iface_cmdq_write(hdev, pkt);
- }
- static int venus_session_get_property(struct venus_inst *inst, u32 ptype)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(inst->core);
- struct hfi_session_get_property_pkt pkt;
- int ret;
- ret = pkt_session_get_property(&pkt, inst, ptype);
- if (ret)
- return ret;
- return venus_iface_cmdq_write(hdev, &pkt);
- }
- static int venus_resume(struct venus_core *core)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(core);
- int ret = 0;
- mutex_lock(&hdev->lock);
- if (!hdev->suspended)
- goto unlock;
- ret = venus_power_on(hdev);
- unlock:
- if (!ret)
- hdev->suspended = false;
- mutex_unlock(&hdev->lock);
- return ret;
- }
- static int venus_suspend_1xx(struct venus_core *core)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(core);
- struct device *dev = core->dev;
- u32 ctrl_status;
- int ret;
- if (!hdev->power_enabled || hdev->suspended)
- return 0;
- mutex_lock(&hdev->lock);
- ret = venus_is_valid_state(hdev);
- mutex_unlock(&hdev->lock);
- if (!ret) {
- dev_err(dev, "bad state, cannot suspend\n");
- return -EINVAL;
- }
- ret = venus_prepare_power_collapse(hdev, true);
- if (ret) {
- dev_err(dev, "prepare for power collapse fail (%d)\n", ret);
- return ret;
- }
- mutex_lock(&hdev->lock);
- if (hdev->last_packet_type != HFI_CMD_SYS_PC_PREP) {
- mutex_unlock(&hdev->lock);
- return -EINVAL;
- }
- ret = venus_are_queues_empty(hdev);
- if (ret < 0 || !ret) {
- mutex_unlock(&hdev->lock);
- return -EINVAL;
- }
- ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0);
- if (!(ctrl_status & CPU_CS_SCIACMDARG0_PC_READY)) {
- mutex_unlock(&hdev->lock);
- return -EINVAL;
- }
- ret = venus_power_off(hdev);
- if (ret) {
- mutex_unlock(&hdev->lock);
- return ret;
- }
- hdev->suspended = true;
- mutex_unlock(&hdev->lock);
- return 0;
- }
- static bool venus_cpu_and_video_core_idle(struct venus_hfi_device *hdev)
- {
- u32 ctrl_status, cpu_status;
- cpu_status = venus_readl(hdev, WRAPPER_CPU_STATUS);
- ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0);
- if (cpu_status & WRAPPER_CPU_STATUS_WFI &&
- ctrl_status & CPU_CS_SCIACMDARG0_INIT_IDLE_MSG_MASK)
- return true;
- return false;
- }
- static bool venus_cpu_idle_and_pc_ready(struct venus_hfi_device *hdev)
- {
- u32 ctrl_status, cpu_status;
- cpu_status = venus_readl(hdev, WRAPPER_CPU_STATUS);
- ctrl_status = venus_readl(hdev, CPU_CS_SCIACMDARG0);
- if (cpu_status & WRAPPER_CPU_STATUS_WFI &&
- ctrl_status & CPU_CS_SCIACMDARG0_PC_READY)
- return true;
- return false;
- }
- static int venus_suspend_3xx(struct venus_core *core)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(core);
- struct device *dev = core->dev;
- bool val;
- int ret;
- if (!hdev->power_enabled || hdev->suspended)
- return 0;
- mutex_lock(&hdev->lock);
- ret = venus_is_valid_state(hdev);
- mutex_unlock(&hdev->lock);
- if (!ret) {
- dev_err(dev, "bad state, cannot suspend\n");
- return -EINVAL;
- }
- /*
- * Power collapse sequence for Venus 3xx and 4xx versions:
- * 1. Check for ARM9 and video core to be idle by checking WFI bit
- * (bit 0) in CPU status register and by checking Idle (bit 30) in
- * Control status register for video core.
- * 2. Send a command to prepare for power collapse.
- * 3. Check for WFI and PC_READY bits.
- */
- ret = readx_poll_timeout(venus_cpu_and_video_core_idle, hdev, val, val,
- 1500, 100 * 1500);
- if (ret)
- return ret;
- ret = venus_prepare_power_collapse(hdev, false);
- if (ret) {
- dev_err(dev, "prepare for power collapse fail (%d)\n", ret);
- return ret;
- }
- ret = readx_poll_timeout(venus_cpu_idle_and_pc_ready, hdev, val, val,
- 1500, 100 * 1500);
- if (ret)
- return ret;
- mutex_lock(&hdev->lock);
- ret = venus_power_off(hdev);
- if (ret) {
- dev_err(dev, "venus_power_off (%d)\n", ret);
- mutex_unlock(&hdev->lock);
- return ret;
- }
- hdev->suspended = true;
- mutex_unlock(&hdev->lock);
- return 0;
- }
- static int venus_suspend(struct venus_core *core)
- {
- if (IS_V3(core) || IS_V4(core))
- return venus_suspend_3xx(core);
- return venus_suspend_1xx(core);
- }
- static const struct hfi_ops venus_hfi_ops = {
- .core_init = venus_core_init,
- .core_deinit = venus_core_deinit,
- .core_ping = venus_core_ping,
- .core_trigger_ssr = venus_core_trigger_ssr,
- .session_init = venus_session_init,
- .session_end = venus_session_end,
- .session_abort = venus_session_abort,
- .session_flush = venus_session_flush,
- .session_start = venus_session_start,
- .session_stop = venus_session_stop,
- .session_continue = venus_session_continue,
- .session_etb = venus_session_etb,
- .session_ftb = venus_session_ftb,
- .session_set_buffers = venus_session_set_buffers,
- .session_unset_buffers = venus_session_unset_buffers,
- .session_load_res = venus_session_load_res,
- .session_release_res = venus_session_release_res,
- .session_parse_seq_hdr = venus_session_parse_seq_hdr,
- .session_get_seq_hdr = venus_session_get_seq_hdr,
- .session_set_property = venus_session_set_property,
- .session_get_property = venus_session_get_property,
- .resume = venus_resume,
- .suspend = venus_suspend,
- .isr = venus_isr,
- .isr_thread = venus_isr_thread,
- };
- void venus_hfi_destroy(struct venus_core *core)
- {
- struct venus_hfi_device *hdev = to_hfi_priv(core);
- venus_interface_queues_release(hdev);
- mutex_destroy(&hdev->lock);
- kfree(hdev);
- core->priv = NULL;
- core->ops = NULL;
- }
- int venus_hfi_create(struct venus_core *core)
- {
- struct venus_hfi_device *hdev;
- int ret;
- hdev = kzalloc(sizeof(*hdev), GFP_KERNEL);
- if (!hdev)
- return -ENOMEM;
- mutex_init(&hdev->lock);
- hdev->core = core;
- hdev->suspended = true;
- core->priv = hdev;
- core->ops = &venus_hfi_ops;
- core->core_caps = ENC_ROTATION_CAPABILITY | ENC_SCALING_CAPABILITY |
- ENC_DEINTERLACE_CAPABILITY |
- DEC_MULTI_STREAM_CAPABILITY;
- ret = venus_interface_queues_init(hdev);
- if (ret)
- goto err_kfree;
- return 0;
- err_kfree:
- kfree(hdev);
- core->priv = NULL;
- core->ops = NULL;
- return ret;
- }
|